Merge remote-tracking branch 'origin/devel' into initallocator-fix

This commit is contained in:
Jacek Sieka 2016-10-12 21:28:40 +08:00
commit 2748da1496
255 changed files with 7536 additions and 4040 deletions

4
.gitignore vendored
View file

@ -1,7 +1,11 @@
* *
!**/ !**/
!*.* !*.*
# Cache
nimcache/ nimcache/
rnimcache/
dnimcache/
*.o *.o
!/icons/*.o !/icons/*.o

61
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,61 @@
image: ubuntu:16.04
stages:
- pre-build
- build
- deploy
- test
.linux_set_path: &linux_set_path_def
before_script:
- export PATH=$(pwd)/bin:$PATH
tags:
- linux
.windows_set_path: &win_set_path_def
before_script:
- set PATH=%CD%\bin;%PATH%
tags:
- windows
build-windows:
stage: build
script:
- ci\build.bat
artifacts:
paths:
- bin\nim.exe
- bin\nimd.exe
- compiler\nim.exe
- koch.exe
expire_in: 1 week
tags:
- windows
deploy-windows:
stage: deploy
script:
- koch.exe winrelease
artifacts:
paths:
- build/*.exe
- build/*.zip
expire_in: 1 week
tags:
- windows
- fast
test-windows:
stage: test
<<: *win_set_path_def
script:
- call ci\deps.bat
- nim c --taintMode:on tests\testament\tester
- tests\testament\tester.exe --pedantic all
tags:
- windows
- fast

View file

@ -28,7 +28,7 @@ script:
- nimble install zip - nimble install zip
- nimble install opengl - nimble install opengl
- nimble install sdl1 - nimble install sdl1
- nimble install jester - nimble install jester@#head
- nimble install niminst - nimble install niminst
- nim c --taintMode:on tests/testament/tester - nim c --taintMode:on tests/testament/tester
- tests/testament/tester --pedantic all - tests/testament/tester --pedantic all

View file

@ -3,7 +3,7 @@ set -e
set -x set -x
if [ ! -e csources/.git ]; then if [ ! -e csources/.git ]; then
git clone --depth 1 git://github.com/nim-lang/csources.git csources git clone --depth 1 https://github.com/nim-lang/csources.git csources
fi fi
cd "csources" cd "csources"

3
build_tools.sh Normal file
View file

@ -0,0 +1,3 @@
./bin/nim c --noNimblePath -p:compiler -o:./bin/nimble dist/nimble/src/nimble.nim
./bin/nim c --noNimblePath -p:compiler -o:./bin/nimsuggest dist/nimsuggest/nimsuggest.nim
./bin/nim c -o:./bin/nimgrep tools/nimgrep.nim

14
ci/build.bat Normal file
View file

@ -0,0 +1,14 @@
REM Some debug info
echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%."
gcc -v
git clone --depth 1 https://github.com/nim-lang/csources.git
cd csources
call build64.bat
cd ..
set PATH=%CD%\bin;%PATH%
nim -v
nim c koch
koch.exe boot
copy bin/nim bin/nimd
koch.exe boot -d:release

15
ci/build.sh Normal file
View file

@ -0,0 +1,15 @@
sh ci/deps.sh
# Build from C sources.
git clone --depth 1 https://github.com/nim-lang/csources.git
cd csources
sh build.sh
cd ..
# Add Nim to the PATH
export PATH=$(pwd)/bin:$PATH
# Bootstrap.
nim -v
nim c koch
./koch boot
cp bin/nim bin/nimd
./koch boot -d:release

4
ci/deps.bat Normal file
View file

@ -0,0 +1,4 @@
nim e install_nimble.nims
nim e tests/test_nimscript.nims
nimble update
nimble install -y zip opengl sdl1 jester@#head niminst

17
ci/deps.sh Normal file
View file

@ -0,0 +1,17 @@
# Some debug info
echo "Running on $CI_RUNNER_ID ($CI_RUNNER_DESCRIPTION) with tags $CI_RUNNER_TAGS."
# Packages
apt-get update -qq
apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev libgc-dev nodejs fasm
gcc -v
fasm -v
export PATH=$(pwd)/bin:$PATH
# Nimble deps
nim e install_nimble.nims
nim e tests/test_nimscript.nims
nimble update
nimble install zip opengl sdl1 jester@#head niminst

62
ci/nsis_build.bat Normal file
View file

@ -0,0 +1,62 @@
REM - Run the full testsuite; tests\testament\tester all
REM - Uncomment the list of changes in news.txt
REM - write a news ticker entry
REM - Update the version
REM - Generate the full docs; koch web0
REM - Generate the installers;
REM - Update the version in system.nim
REM - Test the installers
REM - Tag the release
REM - Merge devel into master
REM - Update csources
set NIMVER=%1
Rem Build -docs file:
koch web0
cd web\upload
7z a -tzip docs-%NIMVER%.zip *.html
move /y docs-%NIMVER%.zip download
cd ..\..
Rem Build .zip file:
rem koch csources -d:release
rem koch xz -d:release
rem move /y build\nim-%NIMVER%.zip web\upload\download
rem Grab C sources and nimsuggest
git clone --depth 1 https://github.com/nim-lang/csources.git
set PATH=%CD%\bin;%PATH%
ReM Build Win32 version:
set PATH=C:\Users\araq\projects\mingw32\bin;%PATH%
cd csources
call build.bat
cd ..
ReM Rebuilding koch is necessary because it uses its pointer size to determine
ReM which mingw link to put in the NSIS installer.
nim c --out:koch_temp koch || exit /b
koch_temp boot -d:release || exit /b
koch_temp nsis -d:release || exit /b
koch_temp zip -d:release || exit /b
dir build
move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b
move /y build\nim_%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b
ReM Build Win64 version:
set PATH=C:\Users\araq\projects\mingw64\bin;%PATH%
cd csources
call build64.bat
cd ..
nim c --out:koch_temp koch || exit /b
koch_temp boot -d:release || exit /b
koch_temp nsis -d:release || exit /b
koch_temp zip -d:release || exit /b
move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b
move /y build\nim_%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b

View file

@ -1,6 +1,6 @@
[Package] [Package]
name = "compiler" name = "compiler"
version = "0.14.3" version = "0.15.1"
author = "Andreas Rumpf" author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library." description = "Compiler package providing the compiler sources as a library."
license = "MIT" license = "MIT"
@ -8,4 +8,4 @@ license = "MIT"
InstallDirs = "doc, compiler" InstallDirs = "doc, compiler"
[Deps] [Deps]
Requires: "nim >= 0.13.0" Requires: "nim >= 0.14.0"

View file

@ -46,7 +46,8 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind case a.kind
of tyObject: of tyObject:
result = isPartOfAux(a.sons[0], b, marker) if a.sons[0] != nil:
result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct: of tyGenericInst, tyDistinct:
result = isPartOfAux(lastSon(a), b, marker) result = isPartOfAux(lastSon(a), b, marker)

View file

@ -437,7 +437,7 @@ type
nfExplicitCall # x.y() was used instead of x.y nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfIsCursor # this node is attached a cursor; used for idetools nfPreventCg # this node should be ignored by the codegen
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
@ -725,10 +725,7 @@ type
s*: TStorageLoc s*: TStorageLoc
flags*: TLocFlags # location's flags flags*: TLocFlags # location's flags
t*: PType # type of location t*: PType # type of location
r*: Rope # rope value of location (code generators) r*: Rope # rope value of location (code generators)
heapRoot*: Rope # keeps track of the enclosing heap object that
# owns this location (required by GC algorithms
# employing heap snapshots or sliding views)
# ---------------- end of backend information ------------------------------ # ---------------- end of backend information ------------------------------
@ -746,10 +743,6 @@ type
TInstantiation* = object TInstantiation* = object
sym*: PSym sym*: PSym
concreteTypes*: seq[PType] concreteTypes*: seq[PType]
usedBy*: seq[int32] # list of modules using the generic
# needed in caas mode for purging the cache
# XXX: it's possible to switch to a
# simple ref count here
compilesId*: CompilesId compilesId*: CompilesId
PInstantiation* = ref TInstantiation PInstantiation* = ref TInstantiation
@ -767,7 +760,6 @@ type
case kind*: TSymKind case kind*: TSymKind
of skType, skGenericParam: of skType, skGenericParam:
typeInstCache*: seq[PType] typeInstCache*: seq[PType]
typScope*: PScope
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] procInstCache*: seq[PInstantiation]
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
@ -862,9 +854,6 @@ type
key*, val*: RootRef key*, val*: RootRef
TPairSeq* = seq[TPair] TPairSeq* = seq[TPair]
TTable* = object # the same as table[PObject] of PObject
counter*: int
data*: TPairSeq
TIdPair* = object TIdPair* = object
key*: PIdObj key*: PIdObj
@ -936,7 +925,7 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField, nfDotSetter, nfDotField,
nfIsRef, nfIsCursor, nfLL} nfIsRef, nfPreventCg, nfLL}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2
@ -1108,12 +1097,6 @@ proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
newSeq(dest.data, len(src.data)) newSeq(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyTable*(dest: var TTable, src: TTable) =
dest.counter = src.counter
if isNil(src.data): return
setLen(dest.data, len(src.data))
for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter dest.counter = src.counter
if isNil(src.data): return if isNil(src.data): return
@ -1327,10 +1310,6 @@ proc initStrTable*(x: var TStrTable) =
proc newStrTable*: TStrTable = proc newStrTable*: TStrTable =
initStrTable(result) initStrTable(result)
proc initTable(x: var TTable) =
x.counter = 0
newSeq(x.data, StartSize)
proc initIdTable*(x: var TIdTable) = proc initIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
@ -1429,6 +1408,7 @@ proc copyNode*(src: PNode): PNode =
result.info = src.info result.info = src.info
result.typ = src.typ result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds): when defined(useNodeIds):
if result.id == nodeIdToDebug: if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
@ -1447,6 +1427,7 @@ proc shallowCopy*(src: PNode): PNode =
result.info = src.info result.info = src.info
result.typ = src.typ result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds): when defined(useNodeIds):
if result.id == nodeIdToDebug: if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
@ -1466,6 +1447,7 @@ proc copyTree*(src: PNode): PNode =
result.info = src.info result.info = src.info
result.typ = src.typ result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds): when defined(useNodeIds):
if result.id == nodeIdToDebug: if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
@ -1511,19 +1493,9 @@ proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
return true return true
result = false result = false
proc replaceSons(n: PNode, oldKind, newKind: TNodeKind) =
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind == oldKind: n.sons[i].kind = newKind
proc sonsNotNil(n: PNode): bool =
for i in countup(0, sonsLen(n) - 1):
if n.sons[i] == nil:
return false
result = true
proc getInt*(a: PNode): BiggestInt = proc getInt*(a: PNode): BiggestInt =
case a.kind case a.kind
of nkIntLit..nkUInt64Lit: result = a.intVal of nkCharLit..nkUInt64Lit: result = a.intVal
else: else:
internalError(a.info, "getInt") internalError(a.info, "getInt")
result = 0 result = 0

View file

@ -31,17 +31,6 @@ proc objectSetIncl*(t: var TObjectSet, obj: RootRef)
proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool
# more are not needed ... # more are not needed ...
# ----------------------- (key, val)-Hashtables ----------------------------
proc tablePut*(t: var TTable, key, val: RootRef)
proc tableGet*(t: TTable, key: RootRef): RootRef
type
TCmpProc* = proc (key, closure: RootRef): bool {.nimcall.} # true if found
proc tableSearch*(t: TTable, key, closure: RootRef,
comparator: TCmpProc): RootRef
# return val as soon as comparator returns true; if this never happens,
# nil is returned
# ----------------------- str table ----------------------------------------- # ----------------------- str table -----------------------------------------
proc strTableContains*(t: TStrTable, n: PSym): bool proc strTableContains*(t: TStrTable, n: PSym): bool
proc strTableAdd*(t: var TStrTable, n: PSym) proc strTableAdd*(t: var TStrTable, n: PSym)
@ -251,20 +240,6 @@ proc symToYamlAux(n: PSym, marker: var IntSet,
indent, maxRecDepth: int): Rope indent, maxRecDepth: int): Rope
proc typeToYamlAux(n: PType, marker: var IntSet, proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): Rope indent, maxRecDepth: int): Rope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
var istr = rspaces(indent + 2)
result = rope("[")
var mycount = 0
for i in countup(0, high(n.data)):
if n.data[i] != nil:
if mycount > 0: add(result, ",")
addf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount)
if mycount > 0: addf(result, "$N$1", [rspaces(indent)])
add(result, "]")
assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[Rope]): Rope = proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
# array of (name, value) pairs # array of (name, value) pairs
@ -463,9 +438,6 @@ proc debug(n: PType) =
proc debug(n: PNode) = proc debug(n: PNode) =
echo($debugTree(n, 0, 100)) echo($debugTree(n, 0, 100))
const
EmptySeq = @[]
proc nextTry(h, maxHash: Hash): Hash = proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times # For any initial h in range(maxHash), repeating that maxHash times
@ -519,55 +491,6 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
inc(t.counter) inc(t.counter)
result = false result = false
proc tableRawGet(t: TTable, key: RootRef): int =
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key == key:
return h
h = nextTry(h, high(t.data))
result = -1
proc tableSearch(t: TTable, key, closure: RootRef,
comparator: TCmpProc): RootRef =
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key == key:
if comparator(t.data[h].val, closure):
# BUGFIX 1
return t.data[h].val
h = nextTry(h, high(t.data))
result = nil
proc tableGet(t: TTable, key: RootRef): RootRef =
var index = tableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
var h: Hash = hashNode(key) and high(data)
while data[h].key != nil:
assert(data[h].key != key)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc tableEnlarge(t: var TTable) =
var n: TPairSeq
newSeq(n, len(t.data) * GrowthFactor)
for i in countup(0, high(t.data)):
if t.data[i].key != nil: tableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
proc tablePut(t: var TTable, key, val: RootRef) =
var index = tableRawGet(t, key)
if index >= 0:
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter): tableEnlarge(t)
tableRawInsert(t.data, key, val)
inc(t.counter)
proc strTableContains(t: TStrTable, n: PSym): bool = proc strTableContains(t: TStrTable, n: PSym): bool =
var h: Hash = n.name.h and high(t.data) # start with real hash value var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil: while t.data[h] != nil:

View file

@ -383,18 +383,28 @@ proc createDb() =
interfHash varchar(256) not null, interfHash varchar(256) not null,
fullHash varchar(256) not null, fullHash varchar(256) not null,
created timestamp not null default (DATETIME('now')), created timestamp not null default (DATETIME('now'))
);""") );""")
db.exec(sql""" db.exec(sql"""
create table if not exists Backend(
id integer primary key,
strongdeps varchar(max) not null,
weakdeps varchar(max) not null,
header varchar(max) not null,
code varchar(max) not null
)
create table if not exists Symbol( create table if not exists Symbol(
id integer primary key, id integer primary key,
module integer not null, module integer not null,
backend integer not null,
name varchar(max) not null, name varchar(max) not null,
data varchar(max) not null, data varchar(max) not null,
created timestamp not null default (DATETIME('now')), created timestamp not null default (DATETIME('now')),
foreign key (module) references module(id) foreign key (module) references Module(id),
foreign key (backend) references Backend(id)
);""") );""")
db.exec(sql""" db.exec(sql"""
@ -409,7 +419,3 @@ proc createDb() =
);""") );""")
#db.exec(sql"""
# --create unique index if not exists TsstNameIx on TestResult(name);
# """, [])

View file

@ -411,7 +411,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
add(result, substr(pat, start, i - 1)) add(result, substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op, a: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
@ -458,7 +458,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call # generates a crappy ObjC call
var op, a: TLoc var op: TLoc
initLocExpr(p, ri.sons[0], op) initLocExpr(p, ri.sons[0], op)
var pl = ~"[" var pl = ~"["
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
@ -536,8 +536,6 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
else: else:
genPrefixCall(p, nil, e, d) genPrefixCall(p, nil, e, d)
postStmtActions(p) postStmtActions(p)
when false:
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure: if ri.sons[0].typ.skipTypes({tyGenericInst}).callConv == ccClosure:
@ -549,6 +547,3 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
else: else:
genPrefixCall(p, le, ri, d) genPrefixCall(p, le, ri, d)
postStmtActions(p) postStmtActions(p)
when false:
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)

View file

@ -30,19 +30,6 @@ proc intLiteral(i: BiggestInt): Rope =
else: else:
result = ~"(IL64(-9223372036854775807) - IL64(1))" result = ~"(IL64(-9223372036854775807) - IL64(1))"
proc int32Literal(i: int): Rope =
if i == int(low(int32)):
result = ~"(-2147483647 -1)"
else:
result = rope(i)
proc genHexLiteral(v: PNode): Rope =
# hex literals are unsigned in C
# so we don't generate hex literals any longer.
if v.kind notin {nkIntLit..nkUInt64Lit}:
internalError(v.info, "genHexLiteral")
result = intLiteral(v.intVal)
proc getStrLit(m: BModule, s: string): Rope = proc getStrLit(m: BModule, s: string): Rope =
discard cgsym(m, "TGenericSeq") discard cgsym(m, "TGenericSeq")
result = getTempName(m) result = getTempName(m)
@ -171,7 +158,6 @@ proc getStorageLoc(n: PNode): TStorageLoc =
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
if needToKeepAlive in flags: keepAlive(p, dest)
elif dest.s == OnHeap: elif dest.s == OnHeap:
# location is on heap # location is on heap
# now the writer barrier is inlined for performance: # now the writer barrier is inlined for performance:
@ -198,7 +184,6 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
addrLoc(dest), rdLoc(src)) addrLoc(dest), rdLoc(src))
if needToKeepAlive in flags: keepAlive(p, dest)
proc asgnComplexity(n: PNode): int = proc asgnComplexity(n: PNode): int =
if n != nil: if n != nil:
@ -218,7 +203,6 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
result.s = a.s result.s = a.s
result.t = t result.t = t
result.r = rdLoc(a) & "." & field result.r = rdLoc(a) & "." & field
result.heapRoot = a.heapRoot
proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
let newflags = let newflags =
@ -268,7 +252,6 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(dest), addrLoc(src), rdLoc(dest)) addrLoc(dest), addrLoc(src), rdLoc(dest))
if needToKeepAlive in flags: keepAlive(p, dest)
else: else:
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t))
@ -299,7 +282,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
if dest.s == OnStack or not usesNativeGC(): if dest.s == OnStack or not usesNativeGC():
linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
if needToKeepAlive in flags: keepAlive(p, dest)
elif dest.s == OnHeap: elif dest.s == OnHeap:
# we use a temporary to care for the dreaded self assignment: # we use a temporary to care for the dreaded self assignment:
var tmp: TLoc var tmp: TLoc
@ -310,7 +292,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
addrLoc(dest), rdLoc(src)) addrLoc(dest), rdLoc(src))
if needToKeepAlive in flags: keepAlive(p, dest)
of tyProc: of tyProc:
if needsComplexAssignment(dest.t): if needsComplexAssignment(dest.t):
# optimize closure assignment: # optimize closure assignment:
@ -400,9 +381,6 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genDeepCopy: " & $ty.kind) else: internalError("genDeepCopy: " & $ty.kind)
proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
if d.k == locNone: getTemp(p, typ, d)
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
if d.k != locNone: if d.k != locNone:
if lfNoDeepCopy in d.flags: genAssignment(p, d, s, {}) if lfNoDeepCopy in d.flags: genAssignment(p, d, s, {})
@ -453,13 +431,6 @@ proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
lineCg(p, cpsStmts, frmt, [rdLoc(a)]) lineCg(p, cpsStmts, frmt, [rdLoc(a)])
proc binaryStmtChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a, b: TLoc
if (d.k != locNone): internalError(e.info, "binaryStmtChar")
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
lineCg(p, cpsStmts, frmt, [rdCharLoc(a), rdCharLoc(b)])
proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
var a, b: TLoc var a, b: TLoc
assert(e.sons[1].typ != nil) assert(e.sons[1].typ != nil)
@ -728,8 +699,6 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
template inheritLocation(d: var TLoc, a: TLoc) = template inheritLocation(d: var TLoc, a: TLoc) =
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
if d.heapRoot == nil:
d.heapRoot = if a.heapRoot != nil: a.heapRoot else: a.r
proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType = proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc): PType =
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
@ -757,6 +726,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
var ty = ty var ty = ty
assert r != nil assert r != nil
while ty != nil: while ty != nil:
ty = ty.skipTypes(skipPtrs)
assert(ty.kind in {tyTuple, tyObject}) assert(ty.kind in {tyTuple, tyObject})
result = lookupInRecord(ty.n, field.name) result = lookupInRecord(ty.n, field.name)
if result != nil: break if result != nil: break
@ -893,7 +863,6 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))
if d.k == locNone: d.s = OnHeap if d.k == locNone: d.s = OnHeap
d.heapRoot = a.r
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.r = rfmt(nil, "(*$1)", a.r) a.r = rfmt(nil, "(*$1)", a.r)
putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
@ -1008,9 +977,8 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
add(p.s(cpsStmts), appends) add(p.s(cpsStmts), appends)
if d.k == locNone: if d.k == locNone:
d = tmp d = tmp
keepAlive(p, tmp)
else: else:
genAssignment(p, d, tmp, {needToKeepAlive}) # no need for deep copying genAssignment(p, d, tmp, {}) # no need for deep copying
gcUsage(e) gcUsage(e)
proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
@ -1047,7 +1015,6 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
rdLoc(dest), rdLoc(a))) rdLoc(dest), rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
rdLoc(dest), lens, rope(L)) rdLoc(dest), lens, rope(L))
keepAlive(p, dest)
add(p.s(cpsStmts), appends) add(p.s(cpsStmts), appends)
gcUsage(e) gcUsage(e)
@ -1067,7 +1034,6 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
rdLoc(a), rdLoc(a),
getTypeDesc(p.module, skipTypes(e.sons[1].typ, abstractVar)), getTypeDesc(p.module, skipTypes(e.sons[1].typ, abstractVar)),
getTypeDesc(p.module, bt)]) getTypeDesc(p.module, bt)])
keepAlive(p, a)
#if bt != b.t: #if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, bt, OnHeap) initLoc(dest, locExpr, bt, OnHeap)
@ -1094,7 +1060,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
genTypeInfo(p.module, refType), genTypeInfo(p.module, refType),
sizeExpr] sizeExpr]
if a.s == OnHeap and usesNativeGC(): if a.s == OnHeap and usesNativeGC():
# use newObjRC1 as an optimization; and we don't need 'keepAlive' either # use newObjRC1 as an optimization
if canFormAcycle(a.t): if canFormAcycle(a.t):
linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc) linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc)
else: else:
@ -1103,7 +1069,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
else: else:
b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) b.r = ropecg(p.module, "($1) #newObj($2, $3)", args)
genAssignment(p, a, b, {needToKeepAlive}) # set the object type: genAssignment(p, a, b, {}) # set the object type:
let bt = skipTypes(refType.sons[0], abstractRange) let bt = skipTypes(refType.sons[0], abstractRange)
genObjectInit(p, cpsStmts, bt, a, false) genObjectInit(p, cpsStmts, bt, a, false)
@ -1134,7 +1100,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
else: else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
genAssignment(p, dest, call, {needToKeepAlive}) genAssignment(p, dest, call, {})
proc genNewSeq(p: BProc, e: PNode) = proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc var a, b: TLoc
@ -1199,7 +1165,6 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
tmp2.k = locTemp tmp2.k = locTemp
tmp2.t = field.loc.t tmp2.t = field.loc.t
tmp2.s = if isRef: OnHeap else: OnStack tmp2.s = if isRef: OnHeap else: OnStack
tmp2.heapRoot = tmp.r
expr(p, it.sons[1], tmp2) expr(p, it.sons[1], tmp2)
if d.k == locNone: if d.k == locNone:
@ -1246,7 +1211,6 @@ proc genNewFinalize(p: BProc, e: PNode) =
a, b, f: TLoc a, b, f: TLoc
refType, bt: PType refType, bt: PType
ti: Rope ti: Rope
oldModule: BModule
refType = skipTypes(e.sons[1].typ, abstractVarRange) refType = skipTypes(e.sons[1].typ, abstractVarRange)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], f) initLocExpr(p, e.sons[2], f)
@ -1256,7 +1220,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
getTypeDesc(p.module, refType), getTypeDesc(p.module, refType),
ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))]) ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))])
genAssignment(p, a, b, {needToKeepAlive}) # set the object type: genAssignment(p, a, b, {}) # set the object type:
bt = skipTypes(refType.lastSon, abstractRange) bt = skipTypes(refType.lastSon, abstractRange)
genObjectInit(p, cpsStmts, bt, a, false) genObjectInit(p, cpsStmts, bt, a, false)
gcUsage(e) gcUsage(e)
@ -1294,7 +1258,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
if not p.module.compileToCpp: if not p.module.compileToCpp:
while t.kind == tyObject and t.sons[0] != nil: while t.kind == tyObject and t.sons[0] != nil:
add(r, ~".Sup") add(r, ~".Sup")
t = skipTypes(t.sons[0], typedescInst) t = skipTypes(t.sons[0], skipPtrs)
if isObjLackingTypeField(t): if isObjLackingTypeField(t):
globalError(x.info, errGenerated, globalError(x.info, errGenerated,
"no 'of' operator available for pure objects") "no 'of' operator available for pure objects")
@ -1366,7 +1330,7 @@ proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
initLocExpr(p, n.sons[1], a) initLocExpr(p, n.sons[1], a)
a.r = ropecg(p.module, frmt, [rdLoc(a)]) a.r = ropecg(p.module, frmt, [rdLoc(a)])
if d.k == locNone: getTemp(p, n.typ, d) if d.k == locNone: getTemp(p, n.typ, d)
genAssignment(p, d, a, {needToKeepAlive}) genAssignment(p, d, a, {})
gcUsage(n) gcUsage(n)
proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
@ -1408,12 +1372,10 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
lineCg(p, cpsStmts, setLenPattern, [ lineCg(p, cpsStmts, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
getTypeDesc(p.module, t.sons[0])]) getTypeDesc(p.module, t.sons[0])])
keepAlive(p, a)
gcUsage(e) gcUsage(e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
keepAlive(p, d)
gcUsage(e) gcUsage(e)
proc genSwap(p: BProc, e: PNode, d: var TLoc) = proc genSwap(p: BProc, e: PNode, d: var TLoc) =
@ -1683,7 +1645,6 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
binaryArith(p, e, d, m) binaryArith(p, e, d, m)
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
var line, filen: Rope
case op case op
of mOr, mAnd: genAndOr(p, e, d, op) of mOr, mAnd: genAndOr(p, e, d, op)
of mNot..mToBiggestInt: unaryArith(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op)
@ -1721,10 +1682,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
getTypeDesc(p.module, ranged), res]) getTypeDesc(p.module, ranged), res])
of mConStrStr: genStrConcat(p, e, d) of mConStrStr: genStrConcat(p, e, d)
of mAppendStrCh: of mAppendStrCh: binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
# strictly speaking we need to generate "keepAlive" here too, but this
# very likely not needed and would slow down the code too much I fear
of mAppendStrStr: genStrAppend(p, e, d) of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem: genSeqElemAppend(p, e, d) of mAppendSeqElem: genSeqElemAppend(p, e, d)
of mEqStr: genStrEquals(p, e, d) of mEqStr: genStrEquals(p, e, d)
@ -1918,7 +1876,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
if not p.module.compileToCpp: if not p.module.compileToCpp:
while t.kind == tyObject and t.sons[0] != nil: while t.kind == tyObject and t.sons[0] != nil:
add(r, ".Sup") add(r, ".Sup")
t = skipTypes(t.sons[0], abstractInst) t = skipTypes(t.sons[0], skipPtrs)
if nilCheck != nil: if nilCheck != nil:
linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
nilCheck, r, genTypeInfo(p.module, dest)) nilCheck, r, genTypeInfo(p.module, dest))
@ -2018,7 +1976,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of skEnumField: of skEnumField:
putIntoDest(p, d, n.typ, rope(sym.position)) putIntoDest(p, d, n.typ, rope(sym.position))
of skVar, skForVar, skResult, skLet: of skVar, skForVar, skResult, skLet:
if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if {sfGlobal, sfThread} * sym.flags != {}:
genVarPrototype(p.module, sym)
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
#echo "FAILED FOR PRCO ", p.prc.name.s #echo "FAILED FOR PRCO ", p.prc.name.s
#echo renderTree(p.prc.ast, {renderIds}) #echo renderTree(p.prc.ast, {renderIds})
@ -2126,11 +2085,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkCaseStmt: genCase(p, n, d) of nkCaseStmt: genCase(p, n, d)
of nkReturnStmt: genReturnStmt(p, n) of nkReturnStmt: genReturnStmt(p, n)
of nkBreakStmt: genBreakStmt(p, n) of nkBreakStmt: genBreakStmt(p, n)
of nkAsgn: genAsgn(p, n, fastAsgn=false) of nkAsgn:
if nfPreventCg notin n.flags:
genAsgn(p, n, fastAsgn=false)
of nkFastAsgn: of nkFastAsgn:
# transf is overly aggressive with 'nkFastAsgn', so we work around here. if nfPreventCg notin n.flags:
# See tests/run/tcnstseq3 for an example that would fail otherwise. # transf is overly aggressive with 'nkFastAsgn', so we work around here.
genAsgn(p, n, fastAsgn=p.prc != nil) # See tests/run/tcnstseq3 for an example that would fail otherwise.
genAsgn(p, n, fastAsgn=p.prc != nil)
of nkDiscardStmt: of nkDiscardStmt:
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
genLineDir(p, n) genLineDir(p, n)

View file

@ -21,7 +21,7 @@ proc registerGcRoot(p: BProc, v: PSym) =
# we register a specialized marked proc here; this has the advantage # we register a specialized marked proc here; this has the advantage
# that it works out of the box for thread local storage then :-) # that it works out of the box for thread local storage then :-)
let prc = genTraverseProcForGlobal(p.module, v) let prc = genTraverseProcForGlobal(p.module, v)
appcg(p.module, p.module.initProc.procSec(cpsStmts), appcg(p.module, p.module.initProc.procSec(cpsInit),
"#nimRegisterGlobalMarker($1);$n", [prc]) "#nimRegisterGlobalMarker($1);$n", [prc])
proc isAssignedImmediately(n: PNode): bool {.inline.} = proc isAssignedImmediately(n: PNode): bool {.inline.} =
@ -293,6 +293,8 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
genLineDir(p, n) genLineDir(p, n)
let lend = getLabel(p) let lend = getLabel(p)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
let it = n.sons[i] let it = n.sons[i]
if it.len == 2: if it.len == 2:
when newScopeForIf: startBlock(p) when newScopeForIf: startBlock(p)
@ -359,6 +361,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) = proc genReturnStmt(p: BProc, t: PNode) =
if nfPreventCg in t.flags: return
p.beforeRetNeeded = true p.beforeRetNeeded = true
genLineDir(p, t) genLineDir(p, t)
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
@ -459,7 +462,6 @@ proc genWhileStmt(p: BProc, t: PNode) =
# significantly worse code # significantly worse code
var var
a: TLoc a: TLoc
labl: TLabel
assert(sonsLen(t) == 2) assert(sonsLen(t) == 2)
inc(p.withinLoop) inc(p.withinLoop)
genLineDir(p, t) genLineDir(p, t)
@ -488,16 +490,20 @@ proc genWhileStmt(p: BProc, t: PNode) =
dec(p.withinLoop) dec(p.withinLoop)
proc genBlock(p: BProc, t: PNode, d: var TLoc) = proc genBlock(p: BProc, n: PNode, d: var TLoc) =
# bug #4505: allocate the temp in the outer scope
# so that it can escape the generated {}:
if not isEmptyType(n.typ) and d.k == locNone:
getTemp(p, n.typ, d)
preserveBreakIdx: preserveBreakIdx:
p.breakIdx = startBlock(p) p.breakIdx = startBlock(p)
if t.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
# named block? # named block?
assert(t.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
var sym = t.sons[0].sym var sym = n.sons[0].sym
sym.loc.k = locOther sym.loc.k = locOther
sym.position = p.breakIdx+1 sym.position = p.breakIdx+1
expr(p, t.sons[1], d) expr(p, n.sons[1], d)
endBlock(p) endBlock(p)
proc genParForStmt(p: BProc, t: PNode) = proc genParForStmt(p: BProc, t: PNode) =
@ -592,6 +598,8 @@ proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
labId, until: int): TLabel = labId, until: int): TLabel =
var lend = getLabel(p) var lend = getLabel(p)
for i in 1..until: for i in 1..until:
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)]) lineF(p, cpsStmts, "LA$1: ;$n", [rope(labId + i)])
if t.sons[i].kind == nkOfBranch: if t.sons[i].kind == nkOfBranch:
var length = sonsLen(t.sons[i]) var length = sonsLen(t.sons[i])
@ -727,6 +735,8 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)]) lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)])
var hasDefault = false var hasDefault = false
for i in splitPoint+1 .. < n.len: for i in splitPoint+1 .. < n.len:
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
var branch = n[i] var branch = n[i]
if branch.kind == nkOfBranch: if branch.kind == nkOfBranch:
genCaseRange(p, branch) genCaseRange(p, branch)
@ -757,16 +767,6 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
else: else:
genOrdinalCase(p, t, d) genOrdinalCase(p, t, d)
proc hasGeneralExceptSection(t: PNode): bool =
var length = sonsLen(t)
var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch):
var blen = sonsLen(t.sons[i])
if blen == 1:
return true
inc(i)
result = false
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# code to generate: # code to generate:
# #
@ -807,6 +807,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
var i = 1 var i = 1
var catchAllPresent = false var catchAllPresent = false
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
let blen = sonsLen(t.sons[i]) let blen = sonsLen(t.sons[i])
if i > 1: addf(p.s(cpsStmts), "else ", []) if i > 1: addf(p.s(cpsStmts), "else ", [])
if blen == 1: if blen == 1:
@ -912,6 +914,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
inc p.inExceptBlock inc p.inExceptBlock
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
var blen = sonsLen(t.sons[i]) var blen = sonsLen(t.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
@ -1089,7 +1093,6 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
proc asgnFieldDiscriminant(p: BProc, e: PNode) = proc asgnFieldDiscriminant(p: BProc, e: PNode) =
var a, tmp: TLoc var a, tmp: TLoc
var dotExpr = e.sons[0] var dotExpr = e.sons[0]
var d: PSym
if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.sons[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.sons[0]
initLocExpr(p, e.sons[0], a) initLocExpr(p, e.sons[0], a)
getTemp(p, a.t, tmp) getTemp(p, a.t, tmp)

View file

@ -73,7 +73,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
lineF(p, cpsStmts, "}$n", []) lineF(p, cpsStmts, "}$n", [])
of tyObject: of tyObject:
for i in countup(0, sonsLen(typ) - 1): for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i]) var x = typ.sons[i]
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
if typ.n != nil: genTraverseProc(c, accessor, typ.n) if typ.n != nil: genTraverseProc(c, accessor, typ.n)
of tyTuple: of tyTuple:
let typ = getUniqueType(typ) let typ = getUniqueType(typ)

View file

@ -475,11 +475,11 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
hasField = true hasField = true
elif m.compileToCpp: elif m.compileToCpp:
appcg(m, result, " : public $1 {$n", appcg(m, result, " : public $1 {$n",
[getTypeDescAux(m, typ.sons[0], check)]) [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
hasField = true hasField = true
else: else:
appcg(m, result, " {$n $1 Sup;$n", appcg(m, result, " {$n $1 Sup;$n",
[getTypeDescAux(m, typ.sons[0], check)]) [getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
hasField = true hasField = true
else: else:
addf(result, " {$n", [name]) addf(result, " {$n", [name])
@ -546,7 +546,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
of tyRef, tyPtr, tyVar: of tyRef, tyPtr, tyVar:
var star = if t.kind == tyVar and tfVarIsPtr notin typ.flags and var star = if t.kind == tyVar and tfVarIsPtr notin typ.flags and
compileToCpp(m): "&" else: "*" compileToCpp(m): "&" else: "*"
var et = t.lastSon var et = typ.skipTypes(abstractInst).lastSon
var etB = et.skipTypes(abstractInst) var etB = et.skipTypes(abstractInst)
if etB.kind in {tyArrayConstr, tyArray, tyOpenArray, tyVarargs}: if etB.kind in {tyArrayConstr, tyArray, tyOpenArray, tyVarargs}:
# this is correct! sets have no proper base type, so we treat # this is correct! sets have no proper base type, so we treat
@ -744,14 +744,6 @@ proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
"void* ClEnv;$n} $1;$n", "void* ClEnv;$n} $1;$n",
[result, rettype, desc]) [result, rettype, desc])
proc getTypeDesc(m: BModule, magic: string): Rope =
var sym = magicsys.getCompilerProc(magic)
if sym != nil:
result = getTypeDesc(m, sym.typ)
else:
rawMessage(errSystemNeeds, magic)
result = nil
proc finishTypeDescriptions(m: BModule) = proc finishTypeDescriptions(m: BModule) =
var i = 0 var i = 0
while i < len(m.typeStack): while i < len(m.typeStack):
@ -820,7 +812,9 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) =
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) = proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) =
var base: Rope var base: Rope
if (sonsLen(typ) > 0) and (typ.sons[0] != nil): if (sonsLen(typ) > 0) and (typ.sons[0] != nil):
base = genTypeInfo(m, typ.sons[0]) var x = typ.sons[0]
if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
base = genTypeInfo(m, x)
else: else:
base = rope("0") base = rope("0")
genTypeInfoAuxBase(m, typ, origType, name, base) genTypeInfoAuxBase(m, typ, origType, name, base)
@ -894,10 +888,11 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) =
else: internalError(n.info, "genObjectFields(nkRecCase)") else: internalError(n.info, "genObjectFields(nkRecCase)")
of nkSym: of nkSym:
var field = n.sym var field = n.sym
addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & if field.bitsize == 0:
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.name = $5;$n", [expr, getTypeDesc(m, typ), "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) "$1.name = $5;$n", [expr, getTypeDesc(m, typ),
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)])
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) = proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) =
@ -909,7 +904,7 @@ proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) =
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
var t = typ.sons[0] var t = typ.sons[0]
while t != nil: while t != nil:
t = t.skipTypes(abstractInst) t = t.skipTypes(skipPtrs)
t.flags.incl tfObjHasKids t.flags.incl tfObjHasKids
t = t.sons[0] t = t.sons[0]
@ -1000,9 +995,6 @@ proc fakeClosureType(owner: PSym): PType =
type type
TTypeInfoReason = enum ## for what do we need the type info? TTypeInfoReason = enum ## for what do we need the type info?
tiNew, ## for 'new' tiNew, ## for 'new'
tiNewSeq, ## for 'newSeq'
tiNonVariantAsgn, ## for generic assignment without variants
tiVariantAsgn ## for generic assignment with variants
include ccgtrav include ccgtrav

View file

@ -167,10 +167,6 @@ proc linefmt(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[Rope]) = args: varargs[Rope]) =
add(p.s(s), indentLine(p, ropecg(p.module, frmt, args))) add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
proc appLineCg(p: BProc, r: var Rope, frmt: FormatStr,
args: varargs[Rope]) =
add(r, indentLine(p, ropecg(p.module, frmt, args)))
proc safeLineNm(info: TLineInfo): int = proc safeLineNm(info: TLineInfo): int =
result = toLinenumber(info) result = toLinenumber(info)
if result < 0: result = 0 # negative numbers are not allowed in #line if result < 0: result = 0 # negative numbers are not allowed in #line
@ -250,7 +246,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
if not p.module.compileToCpp: if not p.module.compileToCpp:
while (s.kind == tyObject) and (s.sons[0] != nil): while (s.kind == tyObject) and (s.sons[0] != nil):
add(r, ".Sup") add(r, ".Sup")
s = skipTypes(s.sons[0], abstractInst) s = skipTypes(s.sons[0], skipPtrs)
linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t)) linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t))
of frEmbedded: of frEmbedded:
# worst case for performance: # worst case for performance:
@ -259,8 +255,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
type type
TAssignmentFlag = enum TAssignmentFlag = enum
needToCopy, needForSubtypeCheck, afDestIsNil, afDestIsNotNil, afSrcIsNil, needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil
afSrcIsNotNil, needToKeepAlive
TAssignmentFlags = set[TAssignmentFlag] TAssignmentFlags = set[TAssignmentFlag]
proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
@ -328,8 +323,9 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
inc(p.labels) inc(p.labels)
result.r = "LOC" & rope(p.labels) result.r = "LOC" & rope(p.labels)
addf(p.blocks[0].sections[cpsLocals], #addf(p.blocks[0].sections[cpsLocals],
"$1 $2;$n", [getTypeDesc(p.module, t), result.r]) # "$1 $2;$n", [getTypeDesc(p.module, t), result.r])
linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
result.k = locTemp result.k = locTemp
#result.a = - 1 #result.a = - 1
result.t = t result.t = t
@ -338,31 +334,6 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
result.flags = {} result.flags = {}
constructLoc(p, result, not needsInit) constructLoc(p, result, not needsInit)
proc keepAlive(p: BProc, toKeepAlive: TLoc) =
when false:
# deactivated because of the huge slowdown this causes; GC will take care
# of interior pointers instead
if optRefcGC notin gGlobalOptions: return
var result: TLoc
var fid = rope(p.gcFrameId)
result.r = "GCFRAME.F" & fid
addf(p.gcFrameType, " $1 F$2;$n",
[getTypeDesc(p.module, toKeepAlive.t), fid])
inc(p.gcFrameId)
result.k = locTemp
#result.a = -1
result.t = toKeepAlive.t
result.s = OnStack
result.flags = {}
if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)):
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(result), rdLoc(toKeepAlive))
else:
useStringh(p.module)
linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(result), addrLoc(toKeepAlive), rdLoc(result))
proc initGCFrame(p: BProc): Rope = proc initGCFrame(p: BProc): Rope =
if p.gcFrameId > 0: result = "struct {$1} GCFRAME;$n" % [p.gcFrameType] if p.gcFrameId > 0: result = "struct {$1} GCFRAME;$n" % [p.gcFrameType]
@ -620,9 +591,6 @@ proc generateHeaders(m: BModule) =
addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)]) addf(m.s[cfsHeaders], "#include $1$N", [rope(it.data)])
it = PStrEntry(it.next) it = PStrEntry(it.next)
proc retIsNotVoid(s: PSym): bool =
result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0])
proc initFrame(p: BProc, procname, filename: Rope): Rope = proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame") discard cgsym(p.module, "nimFrame")
if p.maxFrameLen > 0: if p.maxFrameLen > 0:
@ -649,6 +617,24 @@ proc closureSetup(p: BProc, prc: PSym) =
linefmt(p, cpsStmts, "$1 = ($2) ClEnv;$n", linefmt(p, cpsStmts, "$1 = ($2) ClEnv;$n",
rdLoc(env.loc), getTypeDesc(p.module, env.typ)) rdLoc(env.loc), getTypeDesc(p.module, env.typ))
proc easyResultAsgn(n: PNode): PNode =
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} +
declarativeDefs
case n.kind
of nkStmtList, nkStmtListExpr:
var i = 0
while i < n.len and n[i].kind in harmless: inc i
if i < n.len: result = easyResultAsgn(n[i])
of nkAsgn, nkFastAsgn:
if n[0].kind == nkSym and skResult == n[0].sym.kind:
incl n.flags, nfPreventCg
return n[1]
of nkReturnStmt:
if n.len > 0:
result = easyResultAsgn(n[0])
if result != nil: incl n.flags, nfPreventCg
else: discard
proc genProcAux(m: BModule, prc: PSym) = proc genProcAux(m: BModule, prc: PSym) =
var p = newProc(prc, m) var p = newProc(prc, m)
var header = genProcHeader(m, prc) var header = genProcHeader(m, prc)
@ -660,11 +646,17 @@ proc genProcAux(m: BModule, prc: PSym) =
var res = prc.ast.sons[resultPos].sym # get result symbol var res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]): if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
# declare the result symbol: if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
assignLocalVar(p, res) var decl = localVarDecl(p, res)
assert(res.loc.r != nil) var a: TLoc
initLocExprSingleUse(p, val, a)
linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
else:
# declare the result symbol:
assignLocalVar(p, res)
assert(res.loc.r != nil)
initLocalVar(p, res, immediateAsgn=false)
returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc)) returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
initLocalVar(p, res, immediateAsgn=false)
else: else:
fillResult(res) fillResult(res)
assignParam(p, res) assignParam(p, res)
@ -796,7 +788,7 @@ proc genProc(m: BModule, prc: PSym) =
genProcAux(generatedHeader, prc) genProcAux(generatedHeader, prc)
proc genVarPrototypeAux(m: BModule, sym: PSym) = proc genVarPrototypeAux(m: BModule, sym: PSym) =
assert(sfGlobal in sym.flags) #assert(sfGlobal in sym.flags)
useHeader(m, sym) useHeader(m, sym)
fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap) fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(sym), OnHeap)
if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id): if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
@ -824,12 +816,12 @@ proc addIntTypes(result: var Rope) {.inline.} =
proc getCopyright(cfile: string): Rope = proc getCopyright(cfile: string): Rope =
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) 2015 Andreas Rumpf */$N" & "/* (c) 2016 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N") % "/* The generated code is subject to the original license. */$N") %
[rope(VersionAsString)] [rope(VersionAsString)]
else: else:
result = ("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) 2015 Andreas Rumpf */$N" & "/* (c) 2016 Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N" & "/* The generated code is subject to the original license. */$N" &
"/* Compiled for: $2, $3, $4 */$N" & "/* Compiled for: $2, $3, $4 */$N" &
"/* Command for C compiler:$n $5 */$N") % "/* Command for C compiler:$n $5 */$N") %
@ -875,10 +867,11 @@ proc genMainProc(m: BModule) =
MainProcsWithResult = MainProcsWithResult =
MainProcs & "\treturn nim_program_result;$N" MainProcs & "\treturn nim_program_result;$N"
NimMainBody = NimMainInner = "N_CDECL(void, NimMainInner)(void) {$N" &
"N_CDECL(void, NimMainInner)(void) {$N" &
"$1" & "$1" &
"}$N$N" & "}$N$N"
NimMainProc =
"N_CDECL(void, NimMain)(void) {$N" & "N_CDECL(void, NimMain)(void) {$N" &
"\tvoid (*volatile inner)();$N" & "\tvoid (*volatile inner)();$N" &
"\tPreMain();$N" & "\tPreMain();$N" &
@ -887,6 +880,8 @@ proc genMainProc(m: BModule) =
"\t(*inner)();$N" & "\t(*inner)();$N" &
"}$N$N" "}$N$N"
NimMainBody = NimMainInner & NimMainProc
PosixNimMain = PosixNimMain =
"int cmdCount;$N" & "int cmdCount;$N" &
"char** cmdLine;$N" & "char** cmdLine;$N" &
@ -914,7 +909,7 @@ proc genMainProc(m: BModule) =
" LPSTR lpCmdLine, int nCmdShow) {$N" & " LPSTR lpCmdLine, int nCmdShow) {$N" &
MainProcsWithResult & "}$N$N" MainProcsWithResult & "}$N$N"
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody WinNimDllMain = NimMainInner & "N_LIB_EXPORT " & NimMainProc
WinCDllMain = WinCDllMain =
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
@ -1020,7 +1015,7 @@ proc genInitCode(m: BModule) =
var procname = makeCString(m.module.name.s) var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
else: else:
add(prc, ~"\tTFrame F; FR.len = 0;$N") add(prc, ~"\tTFrame FR; FR.len = 0;$N")
add(prc, genSectionStart(cpsInit)) add(prc, genSectionStart(cpsInit))
add(prc, m.preInitProc.s(cpsInit)) add(prc, m.preInitProc.s(cpsInit))

View file

@ -145,15 +145,16 @@ proc fixupDispatcher(meth, disp: PSym) =
disp.typ.lockLevel = meth.typ.lockLevel disp.typ.lockLevel = meth.typ.lockLevel
proc methodDef*(s: PSym, fromCache: bool) = proc methodDef*(s: PSym, fromCache: bool) =
var L = len(gMethods) let L = len(gMethods)
var witness: PSym var witness: PSym
for i in countup(0, L - 1): for i in countup(0, L - 1):
var disp = gMethods[i].dispatcher let disp = gMethods[i].dispatcher
case sameMethodBucket(disp, s) case sameMethodBucket(disp, s)
of Yes: of Yes:
add(gMethods[i].methods, s) add(gMethods[i].methods, s)
attachDispatcher(s, lastSon(disp.ast)) attachDispatcher(s, lastSon(disp.ast))
fixupDispatcher(s, disp) fixupDispatcher(s, disp)
#echo "fixup ", disp.name.s, " ", disp.id
when useEffectSystem: checkMethodEffects(disp, s) when useEffectSystem: checkMethodEffects(disp, s)
if sfBase in s.flags and gMethods[i].methods[0] != s: if sfBase in s.flags and gMethods[i].methods[0] != s:
# already exists due to forwarding definition? # already exists due to forwarding definition?

View file

@ -27,6 +27,7 @@ type
seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
jArray: JsonNode jArray: JsonNode
types: TStrTable types: TStrTable
isPureRst: bool
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
@ -440,11 +441,6 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[rope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
if k in routineKinds and nameNode.kind == nkSym:
let att = attachToType(d, nameNode.sym)
if att != nil:
dispA(result, """<span class="attachedType" style="visibility:hidden">$1</span>""", "",
[rope esc(d.target, att.name.s)])
inc(d.id) inc(d.id)
let let
plainNameRope = rope(xmltree.escape(plainName.strip)) plainNameRope = rope(xmltree.escape(plainName.strip))
@ -459,25 +455,35 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var seeSrcRope: Rope = nil var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc") let docItemSeeSrc = getConfigVar("doc.item.seesrc")
if docItemSeeSrc.len > 0 and options.docSeeSrcUrl.len > 0: if docItemSeeSrc.len > 0:
let path = n.info.toFilename.extractFilename.rope let cwd = getCurrentDir().canonicalizePath()
let urlRope = ropeFormatNamedVars(options.docSeeSrcUrl, var path = n.info.toFullPath
["path", "line"], [path, rope($n.info.line)]) if path.startsWith(cwd):
path = path[cwd.len+1 .. ^1].replace('\\', '/')
var commit = getConfigVar("git.commit")
if commit.len == 0: commit = "master"
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
["path", "line", "url"], [path, ["path", "line", "url", "commit"], [rope path,
rope($n.info.line), urlRope])]) rope($n.info.line), rope getConfigVar("git.url"),
rope commit])])
add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID", "header_plain", "itemSym", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope])) symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
var attype: Rope
if k in routineKinds and nameNode.kind == nkSym:
let att = attachToType(d, nameNode.sym)
if att != nil:
attype = rope esc(d.target, att.name.s)
add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID", "header_plain", "itemSym", ["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc"], "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"],
[rope(getName(d, nameNode, d.splitAfter)), result, comm, [rope(getName(d, nameNode, d.splitAfter)), result, comm,
itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope,
plainSymbolEncRope, symbolOrIdEncRope])) plainSymbolEncRope, symbolOrIdEncRope, attype]))
# Ironically for types the complexSymbol is *cleaner* than the plainName # Ironically for types the complexSymbol is *cleaner* than the plainName
# because it doesn't include object fields or documentation comments. So we # because it doesn't include object fields or documentation comments. So we
@ -629,7 +635,9 @@ proc genOutFile(d: PDoc): Rope =
# Modules get an automatic title for the HTML, but no entry in the index. # Modules get an automatic title for the HTML, but no entry in the index.
title = "Module " & extractFilename(changeFileExt(d.filename, "")) title = "Module " & extractFilename(changeFileExt(d.filename, ""))
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group"
elif d.hasToc: "doc.body_toc"
else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], "tableofcontents", "moduledesc", "date", "time", "content"],
[title.rope, toc, d.modDesc, rope(getDateStr()), [title.rope, toc, d.modDesc, rope(getDateStr()),
@ -694,6 +702,7 @@ proc commandDoc*() =
proc commandRstAux(filename, outExt: string) = proc commandRstAux(filename, outExt: string) =
var filen = addFileExt(filename, "txt") var filen = addFileExt(filename, "txt")
var d = newDocumentor(filen, options.gConfigVars) var d = newDocumentor(filen, options.gConfigVars)
d.isPureRst = true
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc, var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
{roSupportRawDirective}) {roSupportRawDirective})
var modDesc = newStringOfCap(30_000) var modDesc = newStringOfCap(30_000)

View file

@ -505,10 +505,6 @@ proc noAbsolutePaths: bool {.inline.} =
# `optGenMapping` is included here for niminst. # `optGenMapping` is included here for niminst.
result = gGlobalOptions * {optGenScript, optGenMapping} != {} result = gGlobalOptions * {optGenScript, optGenMapping} != {}
const
specialFileA = 42
specialFileB = 42
var fileCounter: int var fileCounter: int
proc add(s: var string, many: openArray[string]) = proc add(s: var string, many: openArray[string]) =

View file

@ -54,6 +54,6 @@ proc loadMaxIds*(project: string) =
if f.readLine(line): if f.readLine(line):
var frontEndId = parseInt(line) var frontEndId = parseInt(line)
if f.readLine(line): if f.readLine(line):
var backEndId = parseInt(line) # var backEndId = parseInt(line)
gFrontEndId = max(gFrontEndId, frontEndId) gFrontEndId = max(gFrontEndId, frontEndId)
f.close() f.close()

View file

@ -163,7 +163,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
var f = checkModuleName(n) var f = checkModuleName(n)
if f != InvalidFileIDX: if f != InvalidFileIDX:
result = importModuleAs(n, gImportModule(c.module, f)) result = importModuleAs(n, gImportModule(c.module, f))
if result.info.fileIndex == n.info.fileIndex: # we cannot perform this check reliably because of
# test: modules/import_in_config)
if result.info.fileIndex == c.module.info.fileIndex and
result.info.fileIndex == n.info.fileIndex:
localError(n.info, errGenerated, "A module cannot import itself") localError(n.info, errGenerated, "A module cannot import itself")
if sfDeprecated in result.flags: if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s) message(n.info, warnDeprecated, result.name.s)

View file

@ -50,6 +50,7 @@ Files: "readme.txt;install.txt;contributors.txt;copying.txt"
Files: "makefile" Files: "makefile"
Files: "koch.nim" Files: "koch.nim"
Files: "install_nimble.nims" Files: "install_nimble.nims"
Files: "install_tools.nims"
Files: "icons/nim.ico" Files: "icons/nim.ico"
Files: "icons/nim.rc" Files: "icons/nim.rc"
@ -77,7 +78,8 @@ Files: "lib"
[Other] [Other]
Files: "examples" Files: "examples"
#Files: "dist/nimble" Files: "dist/nimble"
Files: "dist/nimsuggest"
Files: "tests" Files: "tests"
@ -87,10 +89,11 @@ Files: "bin/c2nim.exe"
Files: "bin/nimgrep.exe" Files: "bin/nimgrep.exe"
Files: "bin/nimsuggest.exe" Files: "bin/nimsuggest.exe"
Files: "bin/nimble.exe" Files: "bin/nimble.exe"
Files: "bin/makelink.exe"
Files: "bin/*.dll" Files: "bin/*.dll"
Files: "dist/*.dll"
Files: "koch.exe" Files: "koch.exe"
Files: "finish.exe"
; Files: "dist/mingw" ; Files: "dist/mingw"
Files: "start.bat" Files: "start.bat"
BinPath: r"bin;dist\mingw\bin;dist" BinPath: r"bin;dist\mingw\bin;dist"
@ -98,8 +101,8 @@ BinPath: r"bin;dist\mingw\bin;dist"
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip" Download: r"Support DLLs|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip"
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe" Download: r"Aporia Text Editor|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
; for now only NSIS supports optional downloads ; for now only NSIS supports optional downloads
[UnixBin] [UnixBin]
@ -116,7 +119,6 @@ path = r"c:\Program Files (x86)\Inno Setup 5\iscc.exe"
flags = "/Q" flags = "/Q"
[NSIS] [NSIS]
path = r"c:\Program Files (x86)\NSIS\makensis.exe"
flags = "/V0" flags = "/V0"
[C_Compiler] [C_Compiler]

View file

@ -512,6 +512,10 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
arithAux(p, n, r, op, jsOps) arithAux(p, n, r, op, jsOps)
r.kind = resExpr r.kind = resExpr
proc hasFrameInfo(p: PProc): bool =
({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags))
proc genLineDir(p: PProc, n: PNode) = proc genLineDir(p: PProc, n: PNode) =
let line = toLinenumber(n.info) let line = toLinenumber(n.info)
if optLineDir in p.options: if optLineDir in p.options:
@ -521,9 +525,7 @@ proc genLineDir(p: PProc, n: PNode) =
((p.prc == nil) or sfPure notin p.prc.flags): ((p.prc == nil) or sfPure notin p.prc.flags):
useMagic(p, "endb") useMagic(p, "endb")
addf(p.body, "endb($1);$n", [rope(line)]) addf(p.body, "endb($1);$n", [rope(line)])
elif ({optLineTrace, optStackTrace} * p.options == elif hasFrameInfo(p):
{optLineTrace, optStackTrace}) and
((p.prc == nil) or not (sfPure in p.prc.flags)):
addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
proc genWhileStmt(p: PProc, n: PNode) = proc genWhileStmt(p: PProc, n: PNode) =
@ -558,10 +560,13 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate: # code to generate:
# #
# ++excHandler; # ++excHandler;
# var tmpFramePtr = framePtr;
# try { # try {
# stmts; # stmts;
# --excHandler;
# } catch (EXC) { # } catch (EXC) {
# var prevJSError = lastJSError; lastJSError = EXC; # var prevJSError = lastJSError; lastJSError = EXC;
# framePtr = tmpFramePtr;
# --excHandler; # --excHandler;
# if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
# stmts; # stmts;
@ -572,6 +577,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# } # }
# lastJSError = prevJSError; # lastJSError = prevJSError;
# } finally { # } finally {
# framePtr = tmpFramePtr;
# stmts; # stmts;
# } # }
genLineDir(p, n) genLineDir(p, n)
@ -584,8 +590,10 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
if catchBranchesExist and p.target == targetJS: if catchBranchesExist and p.target == targetJS:
add(p.body, "++excHandler;" & tnl) add(p.body, "++excHandler;" & tnl)
var safePoint = "Tmp$1" % [rope(p.unique)] var tmpFramePtr = rope"F"
if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) if optStackTrace notin p.options:
tmpFramePtr = p.getTemp(true)
add(p.body, tmpFramePtr & " = framePtr;" & tnl)
addf(p.body, "try {$n", []) addf(p.body, "try {$n", [])
if p.target == targetPHP and p.globals == nil: if p.target == targetPHP and p.globals == nil:
p.globals = "global $lastJSError; global $prevJSError;".rope p.globals = "global $lastJSError; global $prevJSError;".rope
@ -595,8 +603,9 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
var generalCatchBranchExists = false var generalCatchBranchExists = false
let dollar = rope(if p.target == targetJS: "" else: "$") let dollar = rope(if p.target == targetJS: "" else: "$")
if p.target == targetJS and catchBranchesExist: if p.target == targetJS and catchBranchesExist:
addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" & addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
" lastJSError = EXC;$n --excHandler;$n", []) " lastJSError = EXC;$n --excHandler;$n", [])
add(p.body, "framePtr = $1;$n" % [tmpFramePtr])
elif p.target == targetPHP: elif p.target == targetPHP:
addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
while i < length and n.sons[i].kind == nkExceptBranch: while i < length and n.sons[i].kind == nkExceptBranch:
@ -618,8 +627,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
addf(orExpr, "isObj($2lastJSError.m_type, $1)", addf(orExpr, "isObj($2lastJSError.m_type, $1)",
[genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
if i > 1: add(p.body, "else ") if i > 1: add(p.body, "else ")
addf(p.body, "if ($3lastJSError && ($2)) {$n", addf(p.body, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr])
[safePoint, orExpr, dollar])
gen(p, n.sons[i].sons[blen - 1], a) gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r) moveInto(p, a, r)
addf(p.body, "}$n", []) addf(p.body, "}$n", [])
@ -631,6 +639,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
if p.target == targetJS: if p.target == targetJS:
add(p.body, "} finally {" & tnl) add(p.body, "} finally {" & tnl)
add(p.body, "framePtr = $1;$n" % [tmpFramePtr])
if p.target == targetPHP: if p.target == targetPHP:
# XXX ugly hack for PHP codegen # XXX ugly hack for PHP codegen
add(p.body, "}" & tnl) add(p.body, "}" & tnl)
@ -1355,7 +1364,7 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
if output.len > 0: output.add(", ") if output.len > 0: output.add(", ")
addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)])
while t != nil: while t != nil:
createRecordVarAux(p, t.n, excludedFieldIDs, output) createRecordVarAux(p, t.skipTypes(skipPtrs).n, excludedFieldIDs, output)
t = t.sons[0] t = t.sons[0]
proc arrayTypeForElemType(typ: PType): string = proc arrayTypeForElemType(typ: PType): string =
@ -1462,13 +1471,21 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)] s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
of etyBaseIndex: of etyBaseIndex:
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
if {sfAddrTaken, sfGlobal} * v.flags != {}: if a.typ == etyBaseIndex:
addf(p.body, "var $1 = [$2, $3];$n", if targetBaseIndex:
[v.loc.r, a.address, a.res]) addf(p.body, "var $1 = $2, $1_Idx = $3;$n", [
v.loc.r, a.address, a.res])
else:
addf(p.body, "var $1 = [$2, $3];$n",
[v.loc.r, a.address, a.res])
else: else:
addf(p.body, "var $1 = $2; var $1_Idx = $3;$n", [ if targetBaseIndex:
v.loc.r, a.address, a.res]) let tmp = p.getTemp
addf(p.body, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
[tmp, a.res, v.loc.r])
else:
addf(p.body, "var $1 = $2;$n", [v.loc.r, a.res])
return return
else: else:
s = a.res s = a.res
@ -1918,10 +1935,10 @@ proc frameCreate(p: PProc; procname, filename: Rope): Rope =
procname, filename] procname, filename]
proc frameDestroy(p: PProc): Rope = proc frameDestroy(p: PProc): Rope =
result = rope(("framePtr = framePtr.prev;" | "$framePtr = $framePtr['prev'];") & tnl) result = rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl)
proc genProcBody(p: PProc, prc: PSym): Rope = proc genProcBody(p: PProc, prc: PSym): Rope =
if optStackTrace in prc.options: if hasFrameInfo(p):
result = frameCreate(p, result = frameCreate(p,
makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(prc.owner.name.s & '.' & prc.name.s),
makeJSString(toFilename(prc.info))) makeJSString(toFilename(prc.info)))
@ -1935,7 +1952,7 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
if prc.typ.callConv == ccSysCall and p.target == targetJS: if prc.typ.callConv == ccSysCall and p.target == targetJS:
result = ("try {$n$1} catch (e) {$n" & result = ("try {$n$1} catch (e) {$n" &
" alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result]
if optStackTrace in prc.options: if hasFrameInfo(p):
add(result, frameDestroy(p)) add(result, frameDestroy(p))
proc genProc(oldProc: PProc, prc: PSym): Rope = proc genProc(oldProc: PProc, prc: PSym): Rope =

View file

@ -76,7 +76,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)]) addf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.sons[0] != nil): if (typ.kind == tyObject) and (typ.sons[0] != nil):
addf(p.g.typeInfo, "$1.base = $2;$n", addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])]) [name, genTypeInfo(p, typ.sons[0].skipTypes(skipPtrs))])
proc genTupleFields(p: PProc, typ: PType): Rope = proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = nil var s: Rope = nil

View file

@ -226,8 +226,14 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
template isIterator*(owner: PSym): bool = template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure owner.kind == skIterator and owner.typ.callConv == ccClosure
proc liftingHarmful(owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime
proc liftIterSym*(n: PNode; owner: PSym): PNode = proc liftIterSym*(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env)) # transforms (iter) to (let env = newClosure[iter](); (iter, env))
if liftingHarmful(owner): return n
let iter = n.sym let iter = n.sym
assert iter.isIterator assert iter.isIterator
@ -838,6 +844,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
nkBreakState(cl.state) nkBreakState(cl.state)
... ...
""" """
if liftingHarmful(owner): return body
var L = body.len var L = body.len
if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds): if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
localError(body.info, "ignored invalid for loop") localError(body.info, "ignored invalid for loop")

View file

@ -211,9 +211,6 @@ proc closeLexer*(lex: var TLexer) =
inc(gLinesCompiled, lex.lineNumber) inc(gLinesCompiled, lex.lineNumber)
closeBaseLexer(lex) closeBaseLexer(lex)
proc getColumn(L: TLexer): int =
result = getColNumber(L, L.bufpos)
proc getLineInfo(L: TLexer): TLineInfo = proc getLineInfo(L: TLexer): TLineInfo =
result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos)) result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos))
@ -237,12 +234,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
proc isFloatLiteral(s: string): bool =
for i in countup(0, len(s) - 1):
if s[i] in {'.', 'e', 'E'}:
return true
result = false
{.push overflowChecks: off.} {.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks # We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =

View file

@ -172,7 +172,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
if field != nil: break if field != nil: break
t = t.sons[0] t = t.sons[0]
if t == nil: break if t == nil: break
t = t.skipTypes(abstractInst) t = t.skipTypes(skipPtrs)
#if field == nil: #if field == nil:
# echo "FIELD ", b # echo "FIELD ", b
# debug deref.typ # debug deref.typ
@ -193,7 +193,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
if result != nil: break if result != nil: break
t = t.sons[0] t = t.sons[0]
if t == nil: break if t == nil: break
t = t.skipTypes(abstractInst) t = t.skipTypes(skipPtrs)
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode = proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
# returns a[].b as a node # returns a[].b as a node

View file

@ -38,9 +38,6 @@ proc getModule*(fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < gCompiledModules.len: if fileIdx >= 0 and fileIdx < gCompiledModules.len:
result = gCompiledModules[fileIdx] result = gCompiledModules[fileIdx]
template hash(x: PSym): untyped =
gMemCacheData[x.position].hash
proc hashChanged(fileIdx: int32): bool = proc hashChanged(fileIdx: int32): bool =
internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
@ -189,14 +186,11 @@ proc compileModule*(fileIdx: int32, flags: TSymFlags): PSym =
return return
else: else:
result.id = getID() result.id = getID()
if sfMainModule in flags and gProjectIsStdin: let validFile = processModule(result, if sfMainModule in flags and gProjectIsStdin: llStreamOpen(stdin) else: nil, rd)
processModule(result, llStreamOpen(stdin), rd)
else:
processModule(result, nil, rd)
if optCaasEnabled in gGlobalOptions: if optCaasEnabled in gGlobalOptions:
gMemCacheData[fileIdx].compiledAt = gLastCmdTime gMemCacheData[fileIdx].compiledAt = gLastCmdTime
gMemCacheData[fileIdx].needsRecompile = Recompiled gMemCacheData[fileIdx].needsRecompile = Recompiled
doHash fileIdx if validFile: doHash fileIdx
else: else:
if checkDepMem(fileIdx) == Yes: if checkDepMem(fileIdx) == Yes:
result = compileModule(fileIdx, flags) result = compileModule(fileIdx, flags)
@ -220,12 +214,6 @@ proc includeModule*(s: PSym, fileIdx: int32): PNode {.procvar.} =
addDep(s, fileIdx) addDep(s, fileIdx)
doHash(fileIdx) doHash(fileIdx)
proc `==^`(a, b: string): bool =
try:
result = sameFile(a, b)
except OSError:
result = false
proc compileSystemModule* = proc compileSystemModule* =
if magicsys.systemModule == nil: if magicsys.systemModule == nil:
systemFileIdx = fileInfoIdx(options.libpath/"system.nim") systemFileIdx = fileInfoIdx(options.libpath/"system.nim")

View file

@ -65,7 +65,7 @@ type
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments, errAmbiguousCallXYZ, errWrongNumberOfArguments,
errXCannotBePassedToProcVar, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor, errInvalidOrderInArrayConstructor,
@ -274,7 +274,7 @@ const
errWrongNumberOfArguments: "wrong number of arguments", errWrongNumberOfArguments: "wrong number of arguments",
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
errPragmaOnlyInHeaderOfProc: "pragmas are only allowed in the header of a proc", errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
errImplOfXexpected: "implementation of \'$1\' expected", errImplOfXexpected: "implementation of \'$1\' expected",
errNoSymbolToBorrowFromFound: "no symbol to borrow from found", errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
@ -521,7 +521,7 @@ const
hintCodeBegin, hintCodeEnd, hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace, hintSource, hintStackTrace,
hintGCStats}, hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace}, {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
{low(TNoteKind)..high(TNoteKind)}] {low(TNoteKind)..high(TNoteKind)}]
const const
@ -660,8 +660,6 @@ const
WarningColor = fgYellow WarningColor = fgYellow
HintTitle = "Hint: " HintTitle = "Hint: "
HintColor = fgGreen HintColor = fgGreen
InfoTitle = "Info: "
InfoColor = fgCyan
proc getInfoContextLen*(): int = return msgContext.len proc getInfoContextLen*(): int = return msgContext.len
proc setInfoContextLen*(L: int) = setLen(msgContext, L) proc setInfoContextLen*(L: int) = setLen(msgContext, L)

View file

@ -63,18 +63,6 @@ proc addNimblePath(p: string, info: TLineInfo) =
message(info, hintPath, p) message(info, hintPath, p)
lists.prependStr(options.lazyPaths, 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):
addNimblePath(p, info)
else:
for kind, p2 in walkDir(p):
if hasNimFile(p2): addNimblePath(p2, info)
proc addPathRec(dir: string, info: TLineInfo) = proc addPathRec(dir: string, info: TLineInfo) =
var packages = newStringTable(modeStyleInsensitive) var packages = newStringTable(modeStyleInsensitive)
var pos = dir.len-1 var pos = dir.len-1

View file

@ -13,5 +13,5 @@
const const
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
VersionAsString* = system.NimVersion VersionAsString* = system.NimVersion
RodFileVersion* = "1215" # modify this if the rod-format changes! RodFileVersion* = "1221" # modify this if the rod-format changes!

View file

@ -70,6 +70,12 @@ type # please make sure we have under 32 options
optExcessiveStackTrace # fully qualified module filenames optExcessiveStackTrace # fully qualified module filenames
TGlobalOptions* = set[TGlobalOption] TGlobalOptions* = set[TGlobalOption]
const
harmlessOptions* = {optForceFullMake, optNoLinking, optReportConceptFailures,
optRun, optUseColors, optStdout}
type
TCommands* = enum # Nim's commands TCommands* = enum # Nim's commands
# **keep binary compatible** # **keep binary compatible**
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,

View file

@ -340,26 +340,6 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
if not isKeyword(p.tok.tokType): getTok(p) if not isKeyword(p.tok.tokType): getTok(p)
result = ast.emptyNode result = ast.emptyNode
proc indexExpr(p: var TParser): PNode =
#| indexExpr = expr
result = parseExpr(p)
proc indexExprList(p: var TParser, first: PNode, k: TNodeKind,
endToken: TTokType): PNode =
#| indexExprList = indexExpr ^+ comma
result = newNodeP(k, p)
addSon(result, first)
getTok(p)
optInd(p, result)
while p.tok.tokType notin {endToken, tkEof}:
var a = indexExpr(p)
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
skipComment(p, a)
optPar(p)
eat(p, endToken)
proc colonOrEquals(p: var TParser, a: PNode): PNode = proc colonOrEquals(p: var TParser, a: PNode): PNode =
if p.tok.tokType == tkColon: if p.tok.tokType == tkColon:
result = newNodeP(nkExprColonExpr, p) result = newNodeP(nkExprColonExpr, p)

View file

@ -46,12 +46,6 @@ proc makePass*(open: TPassOpen = nil,
result.close = close result.close = close
result.process = process result.process = process
# This implements a memory preserving scheme: Top level statements are
# processed in a pipeline. The compiler never looks at a whole module
# any longer. However, this is simple to change, as new passes may perform
# whole program optimizations. For now, we avoid it to save a lot of memory.
proc processModule*(module: PSym, stream: PLLStream, rd: PRodReader)
# the semantic checker needs these: # the semantic checker needs these:
var var
gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.} gImportModule*: proc (m: PSym, fileIdx: int32): PSym {.nimcall.}
@ -160,7 +154,8 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
importStmt.addSon str importStmt.addSon str
if not processTopLevelStmt(importStmt, a): break if not processTopLevelStmt(importStmt, a): break
proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) = proc processModule*(module: PSym, stream: PLLStream,
rd: PRodReader): bool {.discardable.} =
var var
p: TParsers p: TParsers
a: TPassContextArray a: TPassContextArray
@ -173,7 +168,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
s = llStreamOpen(filename, fmRead) s = llStreamOpen(filename, fmRead)
if s == nil: if s == nil:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return false
else: else:
s = stream s = stream
while true: while true:
@ -211,4 +206,4 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
var n = loadInitSection(rd) var n = loadInitSection(rd)
for i in countup(0, sonsLen(n) - 1): processTopLevelStmtCached(n.sons[i], a) for i in countup(0, sonsLen(n) - 1): processTopLevelStmtCached(n.sons[i], a)
closePassesCached(a) closePassesCached(a)
result = true

View file

@ -567,11 +567,13 @@ proc deprecatedStmt(c: PContext; pragma: PNode) =
for n in pragma: for n in pragma:
if n.kind in {nkExprColonExpr, nkExprEqExpr}: if n.kind in {nkExprColonExpr, nkExprEqExpr}:
let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
assert dest != nil
let src = considerQuotedIdent(n[0]) let src = considerQuotedIdent(n[0])
let alias = newSym(skAlias, src, dest, n[0].info) let alias = newSym(skAlias, src, dest, n[0].info)
incl(alias.flags, sfExported) incl(alias.flags, sfExported)
if sfCompilerProc in dest.flags: markCompilerProc(alias) if sfCompilerProc in dest.flags: markCompilerProc(alias)
addInterfaceDecl(c, alias) addInterfaceDecl(c, alias)
n.sons[1] = newSymNode(dest)
else: else:
localError(n.info, "key:value pair expected") localError(n.info, "key:value pair expected")
@ -749,7 +751,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it) noVal(it)
incl(sym.flags, sfThread) incl(sym.flags, sfThread)
incl(sym.flags, sfProcvar) incl(sym.flags, sfProcvar)
if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ != nil:
incl(sym.typ.flags, tfThread)
if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault
of wGcSafe: of wGcSafe:
noVal(it) noVal(it)
if sym.kind != skType: incl(sym.flags, sfThread) if sym.kind != skType: incl(sym.flags, sfThread)

View file

@ -146,12 +146,6 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
else: else:
g.pendingWhitespace = s.len g.pendingWhitespace = s.len
proc putLong(g: var TSrcGen, kind: TTokType, s: string, lineLen: int) =
# use this for tokens over multiple lines.
addPendingNL(g)
addTok(g, kind, s)
g.lineLen = lineLen
proc toNimChar(c: char): string = proc toNimChar(c: char): string =
case c case c
of '\0': result = "\\0" of '\0': result = "\\0"
@ -264,9 +258,6 @@ proc pushCom(g: var TSrcGen, n: PNode) =
proc popAllComs(g: var TSrcGen) = proc popAllComs(g: var TSrcGen) =
setLen(g.comStack, 0) setLen(g.comStack, 0)
proc popCom(g: var TSrcGen) =
setLen(g.comStack, len(g.comStack) - 1)
const const
Space = " " Space = " "
@ -492,7 +483,7 @@ proc fits(g: TSrcGen, x: int): bool =
type type
TSubFlag = enum TSubFlag = enum
rfLongMode, rfNoIndent, rfInConstExpr rfLongMode, rfInConstExpr
TSubFlags = set[TSubFlag] TSubFlags = set[TSubFlag]
TContext = tuple[spacing: int, flags: TSubFlags] TContext = tuple[spacing: int, flags: TSubFlags]
@ -675,16 +666,6 @@ proc gfor(g: var TSrcGen, n: PNode) =
gcoms(g) gcoms(g)
gstmts(g, n.sons[length - 1], c) gstmts(g, n.sons[length - 1], c)
proc gmacro(g: var TSrcGen, n: PNode) =
var c: TContext
initContext(c)
gsub(g, n.sons[0])
putWithSpace(g, tkColon, ":")
if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g)
gsons(g, n, c, 1)
proc gcase(g: var TSrcGen, n: PNode) = proc gcase(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
initContext(c) initContext(c)

View file

@ -145,13 +145,7 @@ type
PRodReader* = ref TRodReader PRodReader* = ref TRodReader
var rodCompilerprocs*: TStrTable var rodCompilerprocs*: TStrTable # global because this is needed by magicsys
proc handleSymbolFile*(module: PSym): PRodReader
# global because this is needed by magicsys
proc loadInitSection*(r: PRodReader): PNode
# implementation
proc rawLoadStub(s: PSym) proc rawLoadStub(s: PSym)
@ -206,7 +200,7 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
var id = decodeVInt(r.s, r.pos) var id = decodeVInt(r.s, r.pos)
result.typ = rrGetType(r, id, result.info) result.typ = rrGetType(r, id, result.info)
case result.kind case result.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt64Lit:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
result.intVal = decodeVBiggestInt(r.s, r.pos) result.intVal = decodeVBiggestInt(r.s, r.pos)
@ -324,6 +318,29 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
result.align = decodeVInt(r.s, r.pos).int16 result.align = decodeVInt(r.s, r.pos).int16
else: else:
result.align = 2 result.align = 2
if r.s[r.pos] == '\14':
inc(r.pos)
result.lockLevel = decodeVInt(r.s, r.pos).TLockLevel
else:
result.lockLevel = UnspecifiedLockLevel
if r.s[r.pos] == '\15':
inc(r.pos)
result.destructor = rrGetSym(r, decodeVInt(r.s, r.pos), info)
if r.s[r.pos] == '\16':
inc(r.pos)
result.deepCopy = rrGetSym(r, decodeVInt(r.s, r.pos), info)
if r.s[r.pos] == '\17':
inc(r.pos)
result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
while r.s[r.pos] == '\18':
inc(r.pos)
let x = decodeVInt(r.s, r.pos)
doAssert r.s[r.pos] == '\19'
inc(r.pos)
let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
result.methods.safeAdd((x, y))
decodeLoc(r, result.loc, info) decodeLoc(r, result.loc, info)
while r.s[r.pos] == '^': while r.s[r.pos] == '^':
inc(r.pos) inc(r.pos)
@ -349,6 +366,22 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
inc(r.pos) inc(r.pos)
result.path = decodeNode(r, info) result.path = decodeNode(r, info)
proc decodeInstantiations(r: PRodReader; info: TLineInfo;
s: var seq[PInstantiation]) =
while r.s[r.pos] == '\15':
inc(r.pos)
var ii: PInstantiation
new ii
ii.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
ii.concreteTypes = @[]
while r.s[r.pos] == '\17':
inc(r.pos)
ii.concreteTypes.add rrGetType(r, decodeVInt(r.s, r.pos), info)
if r.s[r.pos] == '\20':
inc(r.pos)
ii.compilesId = decodeVInt(r.s, r.pos)
s.safeAdd ii
proc decodeSym(r: PRodReader, info: TLineInfo): PSym = proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
var var
id: int id: int
@ -423,6 +456,27 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
if r.s[r.pos] == '#': if r.s[r.pos] == '#':
inc(r.pos) inc(r.pos)
result.constraint = decodeNode(r, unknownLineInfo()) result.constraint = decodeNode(r, unknownLineInfo())
case result.kind
of skType, skGenericParam:
while r.s[r.pos] == '\14':
inc(r.pos)
result.typeInstCache.safeAdd rrGetType(r, decodeVInt(r.s, r.pos), result.info)
of routineKinds:
decodeInstantiations(r, result.info, result.procInstCache)
if r.s[r.pos] == '\16':
inc(r.pos)
result.gcUnsafetyReason = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
of skModule, skPackage:
decodeInstantiations(r, result.info, result.usedGenerics)
of skLet, skVar, skField, skForVar:
if r.s[r.pos] == '\18':
inc(r.pos)
result.guard = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
if r.s[r.pos] == '\19':
inc(r.pos)
result.bitsize = decodeVInt(r.s, r.pos).int16
else: discard
if r.s[r.pos] == '(': if r.s[r.pos] == '(':
if result.kind in routineKinds: if result.kind in routineKinds:
result.ast = decodeNodeLazyBody(r, result.info, result) result.ast = decodeNodeLazyBody(r, result.info, result)
@ -566,7 +620,8 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
of "GOPTIONS": of "GOPTIONS":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
if gGlobalOptions != dep: r.reason = rrOptions if gGlobalOptions-harmlessOptions != dep-harmlessOptions:
r.reason = rrOptions
of "CMD": of "CMD":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
@ -580,14 +635,14 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
if not condsyms.isDefined(getIdent(w)): if not condsyms.isDefined(getIdent(w)):
r.reason = rrDefines #MessageOut('not defined, but should: ' + w); r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
if (d != countDefinedSymbols()): r.reason = rrDefines if d != countDefinedSymbols(): r.reason = rrDefines
of "FILES": of "FILES":
inc(r.pos, 2) # skip "(\10" inc(r.pos, 2) # skip "(\10"
inc(r.line) inc(r.line)
while r.s[r.pos] != ')': while r.s[r.pos] != ')':
let relativePath = decodeStr(r.s, r.pos) let finalPath = decodeStr(r.s, r.pos)
let resolvedPath = relativePath.findModule(r.origFile) #let resolvedPath = relativePath.findModule(r.origFile)
let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
r.files.add(finalPath.fileInfoIdx) r.files.add(finalPath.fileInfoIdx)
inc(r.pos) # skip #10 inc(r.pos) # skip #10
inc(r.line) inc(r.line)
@ -683,8 +738,11 @@ proc newRodReader(modfilename: string, hash: SecureHash,
if version != RodFileVersion: if version != RodFileVersion:
# since ROD files are only for caching, no backwards compatibility is # since ROD files are only for caching, no backwards compatibility is
# needed # needed
#echo "expected version ", version, " ", RodFileVersion
result.memfile.close
result = nil result = nil
else: else:
result.memfile.close
result = nil result = nil
proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
@ -762,7 +820,7 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
result = decodeSymSafePos(r, d, info) result = decodeSymSafePos(r, d, info)
if result != nil and result.kind == skStub: rawLoadStub(result) if result != nil and result.kind == skStub: rawLoadStub(result)
proc loadInitSection(r: PRodReader): PNode = proc loadInitSection*(r: PRodReader): PNode =
if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection") if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection")
var oldPos = r.pos var oldPos = r.pos
r.pos = r.initIdx r.pos = r.initIdx
@ -818,9 +876,8 @@ proc checkDep(fileIdx: int32): TReasonForRecompile =
var hash = getHash(fileIdx) var hash = getHash(fileIdx)
gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles
result = rrNone result = rrNone
var r: PRodReader = nil
var rodfile = toGeneratedFile(filename.withPackageName, RodExt) var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
r = newRodReader(rodfile, hash, fileIdx) var r = newRodReader(rodfile, hash, fileIdx)
if r == nil: if r == nil:
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
else: else:
@ -847,7 +904,7 @@ proc checkDep(fileIdx: int32): TReasonForRecompile =
gMods[fileIdx].rd = r gMods[fileIdx].rd = r
gMods[fileIdx].reason = result # now we know better gMods[fileIdx].reason = result # now we know better
proc handleSymbolFile(module: PSym): PRodReader = proc handleSymbolFile*(module: PSym): PRodReader =
let fileIdx = module.fileIdx let fileIdx = module.fileIdx
if optSymbolFiles notin gGlobalOptions: if optSymbolFiles notin gGlobalOptions:
module.id = getID() module.id = getID()
@ -923,7 +980,7 @@ proc writeNode(f: File; n: PNode) =
f.write('^') f.write('^')
f.write(n.typ.id) f.write(n.typ.id)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt64Lit:
if n.intVal != 0: if n.intVal != 0:
f.write('!') f.write('!')
f.write(n.intVal) f.write(n.intVal)

View file

@ -16,8 +16,6 @@ import
condsyms, ropes, idents, securehash, rodread, passes, importer, idgen, condsyms, ropes, idents, securehash, rodread, passes, importer, idgen,
rodutils rodutils
# implementation
type type
TRodWriter = object of TPassContext TRodWriter = object of TPassContext
module: PSym module: PSym
@ -39,13 +37,6 @@ type
PRodWriter = ref TRodWriter PRodWriter = ref TRodWriter
proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter
proc addModDep(w: PRodWriter, dep: string)
proc addInclDep(w: PRodWriter, dep: string)
proc addInterfaceSym(w: PRodWriter, s: PSym)
proc addStmt(w: PRodWriter, n: PNode)
proc writeRod(w: PRodWriter)
proc getDefines(): string = proc getDefines(): string =
result = "" result = ""
for d in definedSymbolNames(): for d in definedSymbolNames():
@ -83,19 +74,20 @@ proc newRodWriter(hash: SecureHash, module: PSym): PRodWriter =
result.converters = "" result.converters = ""
result.methods = "" result.methods = ""
result.init = "" result.init = ""
result.origFile = module.info.toFilename result.origFile = module.info.toFullPath
result.data = newStringOfCap(12_000) result.data = newStringOfCap(12_000)
proc addModDep(w: PRodWriter, dep: string) = proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) =
if w.modDeps.len != 0: add(w.modDeps, ' ') if w.modDeps.len != 0: add(w.modDeps, ' ')
encodeVInt(fileIdx(w, dep), w.modDeps) let resolved = dep.findModule(info.toFullPath)
encodeVInt(fileIdx(w, resolved), w.modDeps)
const const
rodNL = "\x0A" rodNL = "\x0A"
proc addInclDep(w: PRodWriter, dep: string) = proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) =
var resolved = dep.findModule(w.module.info.toFullPath) let resolved = dep.findModule(info.toFullPath)
encodeVInt(fileIdx(w, dep), w.inclDeps) encodeVInt(fileIdx(w, resolved), w.inclDeps)
add(w.inclDeps, " ") add(w.inclDeps, " ")
encodeStr($secureHashFile(resolved), w.inclDeps) encodeStr($secureHashFile(resolved), w.inclDeps)
add(w.inclDeps, rodNL) add(w.inclDeps, rodNL)
@ -108,6 +100,10 @@ proc pushType(w: PRodWriter, t: PType) =
proc pushSym(w: PRodWriter, s: PSym) = proc pushSym(w: PRodWriter, s: PSym) =
# check so that the stack does not grow too large: # check so that the stack does not grow too large:
if iiTableGet(w.index.tab, s.id) == InvalidKey: if iiTableGet(w.index.tab, s.id) == InvalidKey:
when false:
if s.kind == skMethod:
echo "encoding ", s.id, " ", s.name.s
writeStackTrace()
w.sstack.add(s) w.sstack.add(s)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
@ -127,7 +123,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add(',') result.add(',')
encodeVInt(n.info.line, result) encodeVInt(n.info.line, result)
result.add(',') result.add(',')
encodeVInt(fileIdx(w, toFilename(n.info)), result) encodeVInt(fileIdx(w, toFullPath(n.info)), result)
elif fInfo.line != n.info.line: elif fInfo.line != n.info.line:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
@ -147,7 +143,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.typ.id, result) encodeVInt(n.typ.id, result)
pushType(w, n.typ) pushType(w, n.typ)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkUInt64Lit:
if n.intVal != 0: if n.intVal != 0:
result.add('!') result.add('!')
encodeVBiggestInt(n.intVal, result) encodeVBiggestInt(n.intVal, result)
@ -229,6 +225,27 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t.align != 2: if t.align != 2:
add(result, '=') add(result, '=')
encodeVInt(t.align, result) encodeVInt(t.align, result)
if t.lockLevel.ord != UnspecifiedLockLevel.ord:
add(result, '\14')
encodeVInt(t.lockLevel.int16, result)
if t.destructor != nil and t.destructor.id != 0:
add(result, '\15')
encodeVInt(t.destructor.id, result)
pushSym(w, t.destructor)
if t.deepCopy != nil:
add(result, '\16')
encodeVInt(t.deepcopy.id, result)
pushSym(w, t.deepcopy)
if t.assignment != nil:
add(result, '\17')
encodeVInt(t.assignment.id, result)
pushSym(w, t.assignment)
for i, s in items(t.methods):
add(result, '\18')
encodeVInt(i, result)
add(result, '\19')
encodeVInt(s.id, result)
pushSym(w, s)
encodeLoc(w, t.loc, result) encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil: if t.sons[i] == nil:
@ -246,6 +263,19 @@ proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeNode(w, info, lib.path, result) encodeNode(w, info, lib.path, result)
proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
result: var string) =
for t in s:
result.add('\15')
encodeVInt(t.sym.id, result)
pushSym(w, t.sym)
for tt in t.concreteTypes:
result.add('\17')
encodeVInt(tt.id, result)
pushType(w, tt)
result.add('\20')
encodeVInt(t.compilesId, result)
proc encodeSym(w: PRodWriter, s: PSym, result: var string) = proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s == nil: if s == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
@ -266,7 +296,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
result.add(',') result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result) if s.info.line != -1'i16: encodeVInt(s.info.line, result)
result.add(',') result.add(',')
encodeVInt(fileIdx(w, toFilename(s.info)), result) encodeVInt(fileIdx(w, toFullPath(s.info)), result)
if s.owner != nil: if s.owner != nil:
result.add('*') result.add('*')
encodeVInt(s.owner.id, result) encodeVInt(s.owner.id, result)
@ -291,6 +321,31 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s.constraint != nil: if s.constraint != nil:
add(result, '#') add(result, '#')
encodeNode(w, unknownLineInfo(), s.constraint, result) encodeNode(w, unknownLineInfo(), s.constraint, result)
case s.kind
of skType, skGenericParam:
for t in s.typeInstCache:
result.add('\14')
encodeVInt(t.id, result)
pushType(w, t)
of routineKinds:
encodeInstantiations(w, s.procInstCache, result)
if s.gcUnsafetyReason != nil:
result.add('\16')
encodeVInt(s.gcUnsafetyReason.id, result)
pushSym(w, s.gcUnsafetyReason)
of skModule, skPackage:
encodeInstantiations(w, s.usedGenerics, result)
# we don't serialize:
#tab*: TStrTable # interface table for modules
of skLet, skVar, skField, skForVar:
if s.guard != nil:
result.add('\18')
encodeVInt(s.guard.id, result)
pushSym(w, s.guard)
if s.bitsize != 0:
result.add('\19')
encodeVInt(s.bitsize, result)
else: discard
# lazy loading will soon reload the ast lazily, so the ast needs to be # lazy loading will soon reload the ast lazily, so the ast needs to be
# the last entry of a symbol: # the last entry of a symbol:
if s.ast != nil: if s.ast != nil:
@ -298,16 +353,6 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
# it is not necessary, but Nim's heavy compile-time evaluation features # it is not necessary, but Nim's heavy compile-time evaluation features
# make that unfeasible nowadays: # make that unfeasible nowadays:
encodeNode(w, s.info, s.ast, result) encodeNode(w, s.info, s.ast, result)
when false:
var codeAst: PNode = nil
if not astNeeded(s):
codeAst = s.ast.sons[codePos]
# ugly hack to not store the AST:
s.ast.sons[codePos] = ast.emptyNode
encodeNode(w, s.info, s.ast, result)
if codeAst != nil:
# resore the AST:
s.ast.sons[codePos] = codeAst
proc addToIndex(w: var TIndex, key, val: int) = proc addToIndex(w: var TIndex, key, val: int) =
if key - w.lastIdxKey == 1: if key - w.lastIdxKey == 1:
@ -562,13 +607,15 @@ proc process(c: PPassContext, n: PNode): PNode =
# addInterfaceSym(w, a.sons[j].sym); # addInterfaceSym(w, a.sons[j].sym);
# end # end
of nkImportStmt: of nkImportStmt:
for i in countup(0, sonsLen(n) - 1): addModDep(w, getModuleName(n.sons[i])) for i in countup(0, sonsLen(n) - 1):
addModDep(w, getModuleName(n.sons[i]), n.info)
addStmt(w, n) addStmt(w, n)
of nkFromStmt: of nkFromStmt, nkImportExceptStmt:
addModDep(w, getModuleName(n.sons[0])) addModDep(w, getModuleName(n.sons[0]), n.info)
addStmt(w, n) addStmt(w, n)
of nkIncludeStmt: of nkIncludeStmt:
for i in countup(0, sonsLen(n) - 1): addInclDep(w, getModuleName(n.sons[i])) for i in countup(0, sonsLen(n) - 1):
addInclDep(w, getModuleName(n.sons[i]), n.info)
of nkPragma: of nkPragma:
addStmt(w, n) addStmt(w, n)
else: else:

View file

@ -310,15 +310,19 @@ proc equalsFile*(r: Rope, f: File): bool =
buf: array[bufSize, char] buf: array[bufSize, char]
bpos = buf.len bpos = buf.len
blen = buf.len blen = buf.len
btotal = 0
rtotal = 0
for s in leaves(r): for s in leaves(r):
var spos = 0 var spos = 0
let slen = s.len let slen = s.len
rtotal += slen
while spos < slen: while spos < slen:
if bpos == blen: if bpos == blen:
# Read more data # Read more data
bpos = 0 bpos = 0
blen = readBuffer(f, addr(buf[0]), buf.len) blen = readBuffer(f, addr(buf[0]), buf.len)
btotal += blen
if blen == 0: # no more data in file if blen == 0: # no more data in file
result = false result = false
return return
@ -330,7 +334,8 @@ proc equalsFile*(r: Rope, f: File): bool =
spos += n spos += n
bpos += n bpos += n
result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all result = readBuffer(f, addr(buf[0]), 1) == 0 and
btotal == rtotal # check that we've read all
proc equalsFile*(r: Rope, filename: string): bool = proc equalsFile*(r: Rope, filename: string): bool =
## returns true if the contents of the file `f` equal `r`. If `f` does not ## returns true if the contents of the file `f` equal `r`. If `f` does not

View file

@ -43,6 +43,7 @@ proc setupVM*(module: PSym; scriptName: string): PEvalContext =
template cbos(name, body) {.dirty.} = template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name), result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) = proc (a: VmArgs) =
errorMsg = nil
try: try:
body body
except OSError: except OSError:
@ -150,7 +151,7 @@ proc runNimScript*(scriptName: string; freshDefines=true) =
vm.globalCtx = setupVM(m, scriptName) vm.globalCtx = setupVM(m, scriptName)
compileSystemModule() compileSystemModule()
processModule(m, llStreamOpen(scriptName, fmRead), nil) discard processModule(m, llStreamOpen(scriptName, fmRead), nil)
# ensure we load 'system.nim' again for the real non-config stuff! # ensure we load 'system.nim' again for the real non-config stuff!
resetAllModulesHard() resetAllModulesHard()

View file

@ -39,7 +39,6 @@ proc semStmt(c: PContext, n: PNode): PNode
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind) proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc maybeAddResult(c: PContext, s: PSym, 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 tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc activate(c: PContext, n: PNode) proc activate(c: PContext, n: PNode)
proc semQuoteAst(c: PContext, n: PNode): PNode proc semQuoteAst(c: PContext, n: PNode): PNode
@ -47,6 +46,10 @@ proc finishMethod(c: PContext, s: PSym)
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc isArrayConstr(n: PNode): bool {.inline.} =
result = n.kind == nkBracket and
n.typ.skipTypes(abstractInst).kind == tyArray
template semIdeForTemplateOrGenericCheck(n, requiresCheck) = template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
# we check quickly if the node is where the cursor is # we check quickly if the node is where the cursor is
when defined(nimsuggest): when defined(nimsuggest):
@ -187,7 +190,6 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
# identifier with visibility # identifier with visibility
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
proc semStmtScope(c: PContext, n: PNode): PNode
proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) = proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
let t = typeAllowed(typ, kind) let t = typeAllowed(typ, kind)
@ -252,7 +254,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
result = arg result = arg
# for 'tcnstseq' we support [] to become 'seq' # for 'tcnstseq' we support [] to become 'seq'
if eOrig.typ.skipTypes(abstractInst).kind == tySequence and if eOrig.typ.skipTypes(abstractInst).kind == tySequence and
arg.typ.skipTypes(abstractInst).kind == tyArrayConstr: isArrayConstr(arg):
arg.typ = eOrig.typ arg.typ = eOrig.typ
proc tryConstExpr(c: PContext, n: PNode): PNode = proc tryConstExpr(c: PContext, n: PNode): PNode =
@ -445,6 +447,8 @@ proc isImportSystemStmt(n: PNode): bool =
else: discard else: discard
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if n.kind == nkDefer:
localError(n.info, "defer statement not supported at top level")
if c.topStmts == 0 and not isImportSystemStmt(n): if c.topStmts == 0 and not isImportSystemStmt(n):
if sfSystemModule notin c.module.flags and if sfSystemModule notin c.module.flags and
n.kind notin {nkEmpty, nkCommentStmt}: n.kind notin {nkEmpty, nkCommentStmt}:

View file

@ -203,7 +203,8 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyObject, tyDistinct: of tyObject, tyDistinct:
if not considerOverloadedOp(c, t, body, x, y): if not considerOverloadedOp(c, t, body, x, y):
if t.sons[0] != nil: liftBodyAux(c, t.sons[0], body, x, y) if t.sons[0] != nil:
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y) if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
of tyTuple: of tyTuple:
liftBodyTup(c, t, body, x, y) liftBodyTup(c, t, body, x, y)

View file

@ -34,6 +34,22 @@ proc sameMethodDispatcher(a, b: PSym): bool =
proc determineType(c: PContext, s: PSym) proc determineType(c: PContext, s: PSym)
proc initCandidateSymbols(c: PContext, headSymbol: PNode,
initialBinding: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
o: var TOverloadIter): seq[tuple[s: PSym, scope: int]] =
result = @[]
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in filter:
result.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if result.len > 0:
initCandidate(c, best, result[0].s, initialBinding, result[0].scope)
initCandidate(c, alt, result[0].s, initialBinding, result[0].scope)
best.state = csNoMatch
proc pickBestCandidate(c: PContext, headSymbol: PNode, proc pickBestCandidate(c: PContext, headSymbol: PNode,
n, orig: PNode, n, orig: PNode,
initialBinding: PNode, initialBinding: PNode,
@ -41,66 +57,62 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors) = errors: var CandidateErrors) =
var o: TOverloadIter var o: TOverloadIter
# thanks to the lazy semchecking for operands, we need to iterate over the var sym = initOverloadIter(o, c, headSymbol)
# symbol table *before* any call to 'initCandidate' which might invoke var scope = o.lastOverloadScope
# semExpr which might modify the symbol table in cases like # Thanks to the lazy semchecking for operands, we need to check whether
# 'init(a, 1, (var b = new(Type2); b))'. # 'initCandidate' modifies the symbol table (via semExpr).
var symx = initOverloadIter(o, c, headSymbol) # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
let symScope = o.lastOverloadScope let counterInitial = c.currentScope.symbols.counter
var syms: seq[tuple[s: PSym, scope: int]]
var syms: seq[tuple[a: PSym, b: int]] = @[] var nextSymIndex = 0
while symx != nil: while sym != nil:
if symx.kind in filter: if sym.kind in filter:
syms.add((symx, o.lastOverloadScope)) # Initialise 'best' and 'alt' with the first available symbol
symx = nextOverloadIter(o, c, headSymbol) initCandidate(c, best, sym, initialBinding, scope)
if syms.len == 0: initCandidate(c, alt, sym, initialBinding, scope)
when false: best.state = csNoMatch
if skIterator notin filter: break
# also try iterators, but these are 2nd class:
symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind == skIterator:
syms.add((symx, 100))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
else: else:
return sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope
var z: TCandidate var z: TCandidate
initCandidate(c, best, syms[0][0], initialBinding, symScope) while sym != nil:
initCandidate(c, alt, syms[0][0], initialBinding, symScope) if sym.kind notin filter:
best.state = csNoMatch sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope
for i in 0 .. <syms.len: continue
let sym = syms[i][0]
determineType(c, sym) determineType(c, sym)
initCandidate(c, z, sym, initialBinding, syms[i][1]) initCandidate(c, z, sym, initialBinding, scope)
if c.currentScope.symbols.counter == counterInitial or syms != nil:
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: matches(c, n, orig, z)
# gDebug = true if errors != nil:
matches(c, n, orig, z) errors.safeAdd((sym, int z.mutabilityProblem))
if errors != nil: if z.errors != nil:
errors.safeAdd((sym, int z.mutabilityProblem)) for err in z.errors:
if z.errors != nil: errors.add(err)
for err in z.errors: if z.state == csMatch:
errors.add(err) # little hack so that iterators are preferred over everything else:
if z.state == csMatch: if sym.kind == skIterator: inc(z.exactMatches, 200)
# little hack so that iterators are preferred over everything else: case best.state
if sym.kind == skIterator: inc(z.exactMatches, 200) of csEmpty, csNoMatch: best = z
case best.state of csMatch:
of csEmpty, csNoMatch: best = z var cmp = cmpCandidates(best, z)
of csMatch: if cmp < 0: best = z # x is better than the best so far
var cmp = cmpCandidates(best, z) elif cmp == 0: alt = z # x is as good as the best so far
if cmp < 0: best = z # x is better than the best so far else:
elif cmp == 0: alt = z # x is as good as the best so far # Symbol table has been modified. Restart and pre-calculate all syms
else: discard # before any further candidate init and compare. SLOW, but rare case.
#if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516: syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o)
# echo "Matches ", n.info, " ", typeToString(sym.typ) if syms == nil:
# debug sym sym = nextOverloadIter(o, c, headSymbol)
# writeMatches(z) scope = o.lastOverloadScope
# for i in 1 .. <len(z.call): elif nextSymIndex < syms.len:
# z.call[i].typ.debug # rare case: retrieve the next pre-calculated symbol
# quit 1 sym = syms[nextSymIndex].s
scope = syms[nextSymIndex].scope
nextSymIndex += 1
else:
break
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,
@ -203,6 +215,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if result.state != csMatch: if result.state != csMatch:
n.sons.delete(1) n.sons.delete(1)
orig.sons.delete(1) orig.sons.delete(1)
excl n.flags, nfExprCall
else: return else: return
if nfDotField in n.flags: if nfDotField in n.flags:
@ -462,3 +475,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
var resolved = semOverloadedCall(c, call, call, {fn.kind}) var resolved = semOverloadedCall(c, call, call, {fn.kind})
if resolved != nil: if resolved != nil:
result = resolved.sons[0].sym result = resolved.sons[0].sym
if not compareTypes(result.typ.sons[0], fn.typ.sons[0], dcEqIgnoreDistinct):
result = nil
elif result.magic in {mArrPut, mArrGet}:
# cannot borrow these magics for now
result = nil

View file

@ -136,8 +136,10 @@ proc isCastable(dst, src: PType): bool =
# tyProc, tySet, tyEnum, tyBool, tyChar} # tyProc, tySet, tyEnum, tyBool, tyChar}
if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray: if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray:
return false return false
var dstSize, srcSize: BiggestInt if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc:
return false
var dstSize, srcSize: BiggestInt
dstSize = computeSize(dst) dstSize = computeSize(dst)
srcSize = computeSize(src) srcSize = computeSize(src)
if dstSize < 0: if dstSize < 0:
@ -409,13 +411,11 @@ proc changeType(n: PNode, newType: PType, check: bool) =
n.typ = newType n.typ = newType
proc arrayConstrType(c: PContext, n: PNode): PType = proc arrayConstrType(c: PContext, n: PNode): PType =
var typ = newTypeS(tyArrayConstr, c) var typ = newTypeS(tyArray, c)
rawAddSon(typ, nil) # index type rawAddSon(typ, nil) # index type
if sonsLen(n) == 0: if sonsLen(n) == 0:
rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
else: else:
var x = n.sons[0]
var lastIndex: BiggestInt = sonsLen(n) - 1
var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal}) var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyOrdinal})
addSonSkipIntLit(typ, t) addSonSkipIntLit(typ, t)
typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info)
@ -423,7 +423,7 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeI(nkBracket, n.info) result = newNodeI(nkBracket, n.info)
result.typ = newTypeS(tyArrayConstr, c) result.typ = newTypeS(tyArray, c)
rawAddSon(result.typ, nil) # index type rawAddSon(result.typ, nil) # index type
if sonsLen(n) == 0: if sonsLen(n) == 0:
rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
@ -468,59 +468,11 @@ proc fixAbstractType(c: PContext, n: PNode) =
if it.kind == nkHiddenSubConv and if it.kind == nkHiddenSubConv and
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}: skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
if skipTypes(it.sons[1].typ, abstractVar).kind in if skipTypes(it.sons[1].typ, abstractVar).kind in
{tyNil, tyArrayConstr, tyTuple, tySet}: {tyNil, tyTuple, tySet} or it[1].isArrayConstr:
var s = skipTypes(it.typ, abstractVar) var s = skipTypes(it.typ, abstractVar)
if s.kind != tyExpr: if s.kind != tyExpr:
changeType(it.sons[1], s, check=true) changeType(it.sons[1], s, check=true)
n.sons[i] = it.sons[1] n.sons[i] = it.sons[1]
when false:
# XXX finally rewrite that crap!
for i in countup(1, sonsLen(n) - 1):
var it = n.sons[i]
case it.kind
of nkHiddenStdConv, nkHiddenSubConv:
if it.sons[1].kind == nkBracket:
it.sons[1].typ = arrayConstrType(c, it.sons[1])
#it.sons[1] = semArrayConstr(c, it.sons[1])
if skipTypes(it.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
#if n.sons[0].kind == nkSym and IdentEq(n.sons[0].sym.name, "[]="):
# debug(n)
var s = skipTypes(it.sons[1].typ, abstractVar)
if s.kind == tyArrayConstr and s.sons[1].kind == tyEmpty:
s = copyType(s, getCurrOwner(), false)
skipTypes(s, abstractVar).sons[1] = elemType(
skipTypes(it.typ, abstractVar))
it.sons[1].typ = s
elif s.kind == tySequence and s.sons[0].kind == tyEmpty:
s = copyType(s, getCurrOwner(), false)
skipTypes(s, abstractVar).sons[0] = elemType(
skipTypes(it.typ, abstractVar))
it.sons[1].typ = s
elif skipTypes(it.sons[1].typ, abstractVar).kind in
{tyNil, tyArrayConstr, tyTuple, tySet}:
var s = skipTypes(it.typ, abstractVar)
if s.kind != tyExpr:
changeType(it.sons[1], s, check=true)
n.sons[i] = it.sons[1]
of nkBracket:
# an implicitly constructed array (passed to an open array):
n.sons[i] = semArrayConstr(c, it, {})
else:
discard
#if (it.typ == nil):
# InternalError(it.info, "fixAbstractType: " & renderTree(it))
proc skipObjConv(n: PNode): PNode =
case n.kind
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if skipTypes(n.sons[1].typ, abstractPtrs).kind in {tyTuple, tyObject}:
result = n.sons[1]
else:
result = n
of nkObjUpConv, nkObjDownConv: result = n.sons[0]
else: result = n
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
@ -701,9 +653,6 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
# error correction, prevents endless for loop elimination in transf. # error correction, prevents endless for loop elimination in transf.
# See bug #2051: # See bug #2051:
result.sons[0] = newSymNode(errorSym(c, n)) result.sons[0] = newSymNode(errorSym(c, n))
if sfNoSideEffect notin callee.flags:
if {sfImportc, sfSideEffect} * callee.flags != {}:
incl(c.p.owner.flags, sfSideEffect)
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
@ -804,9 +753,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
result = m.call result = m.call
instGenericConvertersSons(c, result, m) 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)
elif t != nil and t.kind == tyTypeDesc: elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags) if n.len == 1: return semObjConstr(c, n, flags)
return semConv(c, n) return semConv(c, n)
@ -986,7 +932,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
tyTuple, tySet, tyUInt..tyUInt64: tyTuple, tySet, tyUInt..tyUInt64:
if s.magic == mNone: result = inlineConst(n, s) if s.magic == mNone: result = inlineConst(n, s)
else: result = newSymNode(s, n.info) else: result = newSymNode(s, n.info)
of tyArrayConstr, tySequence: of tyArray, tySequence:
# Consider:: # Consider::
# const x = [] # const x = []
# proc p(a: openarray[int]) # proc p(a: openarray[int])
@ -1040,9 +986,6 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
markUsed(n.info, s) markUsed(n.info, s)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
# if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags:
incl(c.p.owner.flags, sfSideEffect)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still # We cannot check for access to outer vars for example because it's still
# not sure the symbol really ends up being used: # not sure the symbol really ends up being used:
@ -1091,7 +1034,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
result = check result = check
return result return result
if ty.sons[0] == nil: break if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], {tyGenericInst}) ty = skipTypes(ty.sons[0], skipPtrs)
# old code, not sure if it's live code: # old code, not sure if it's live code:
markUsed(n.info, s) markUsed(n.info, s)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
@ -1169,7 +1112,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check) f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check)
if f != nil: break if f != nil: break
if ty.sons[0] == nil: break if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], {tyGenericInst}) ty = skipTypes(ty.sons[0], skipPtrs)
if f != nil: if f != nil:
if fieldVisible(c, f): if fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend? # is the access to a public field or in the same module or in a friend?
@ -1421,8 +1364,9 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
# a = b # both are vars, means: a[] = b[] # a = b # both are vars, means: a[] = b[]
# a = b # b no 'var T' means: a = addr(b) # a = b # b no 'var T' means: a = addr(b)
var le = a.typ var le = a.typ
if skipTypes(le, {tyGenericInst}).kind != tyVar and if (skipTypes(le, {tyGenericInst}).kind != tyVar and
isAssignable(c, a) == arNone: isAssignable(c, a) == arNone) or
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed! # Direct assignment to a discriminant is allowed!
localError(a.info, errXCannotBeAssignedTo, localError(a.info, errXCannotBeAssignedTo,
renderTree(a, {renderNoComments})) renderTree(a, {renderNoComments}))
@ -2128,7 +2072,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
f = lookupInRecordAndBuildCheck(c, it, t.n, id, check) f = lookupInRecordAndBuildCheck(c, it, t.n, id, check)
if f != nil: break if f != nil: break
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], skipPtrs)
if f != nil and fieldVisible(c, f): if f != nil and fieldVisible(c, f):
it.sons[0] = newSymNode(f) it.sons[0] = newSymNode(f)
e = fitNode(c, f.typ, e) e = fitNode(c, f.typ, e)
@ -2145,7 +2089,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
while true: while true:
checkInitialized(objType.n, ids, n.info) checkInitialized(objType.n, ids, n.info)
if objType.sons[0] == nil: break if objType.sons[0] == nil: break
objType = skipTypes(objType.sons[0], {tyGenericInst}) objType = skipTypes(objType.sons[0], skipPtrs)
proc semBlock(c: PContext, n: PNode): PNode = proc semBlock(c: PContext, n: PNode): PNode =
result = n result = n

View file

@ -152,7 +152,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
while t.kind == tyObject: while t.kind == tyObject:
semForObjectFields(fc, t.n, n, stmts) semForObjectFields(fc, t.n, n, stmts)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], abstractPtrs) t = skipTypes(t.sons[0], skipPtrs)
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
# for TR macros this 'while true: ...; break' loop is pretty bad, so # for TR macros this 'while true: ...; break' loop is pretty bad, so
# we avoid it now if we can: # we avoid it now if we can:

View file

@ -458,32 +458,6 @@ proc getConstIfExpr(c: PSym, n: PNode): PNode =
if result == nil: result = getConstExpr(c, it.sons[0]) if result == nil: result = getConstExpr(c, it.sons[0])
else: internalError(it.info, "getConstIfExpr()") else: internalError(it.info, "getConstIfExpr()")
proc partialAndExpr(c: PSym, n: PNode): PNode =
# partial evaluation
result = n
var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2])
if a != nil:
if getInt(a) == 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
if getInt(b) == 0: result = b
else: result = n.sons[1]
proc partialOrExpr(c: PSym, n: PNode): PNode =
# partial evaluation
result = n
var a = getConstExpr(c, n.sons[1])
var b = getConstExpr(c, n.sons[2])
if a != nil:
if getInt(a) != 0: result = a
elif b != nil: result = b
else: result = n.sons[2]
elif b != nil:
if getInt(b) != 0: result = b
else: result = n.sons[1]
proc leValueConv(a, b: PNode): bool = proc leValueConv(a, b: PNode): bool =
result = false result = false
case a.kind case a.kind
@ -540,7 +514,8 @@ proc foldConv*(n, a: PNode; check = false): PNode =
else: else:
result = a result = a
result.typ = n.typ result.typ = n.typ
if check: rangeCheck(n, result.intVal) if check and result.kind in {nkCharLit..nkUInt64Lit}:
rangeCheck(n, result.intVal)
of tyFloat..tyFloat64: of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar: of tyInt..tyInt64, tyEnum, tyBool, tyChar:

View file

@ -47,7 +47,7 @@ proc rawHandleSelf(c: PContext; owner: PSym) =
while t.kind == tyObject: while t.kind == tyObject:
addObjFieldsToLocalScope(c, t.n) addObjFieldsToLocalScope(c, t.n)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = t.sons[0].skipTypes(abstractPtrs) t = t.sons[0].skipTypes(skipPtrs)
proc pushProcCon*(c: PContext; owner: PSym) = proc pushProcCon*(c: PContext; owner: PSym) =
rawPushProcCon(c, owner) rawPushProcCon(c, owner)
@ -97,22 +97,6 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
if inst.compilesId == id and sameInstantiation(entry, inst[]): if inst.compilesId == id and sameInstantiation(entry, inst[]):
return inst.sym return inst.sym
proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly
# and they are not needed anyway for instantiation (each param is already
# provided).
when false:
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
if a.kind != nkIdentDefs: IllFormedAst(a)
var L = a.len
if a.sons[L-1].kind != nkEmpty and a.sons[L-2].kind != nkEmpty:
# ``param: typ = defaultVal``.
# We don't need defaultVal for semantic checking and it's wrong for
# ``cmp: proc (a, b: T): int = cmp``. Hm, for ``cmp = cmp`` that is
# not possible... XXX We don't solve this issue here.
a.sons[L-1] = ast.emptyNode
proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
# we need to create a fresh set of gensym'ed symbols: # we need to create a fresh set of gensym'ed symbols:
if n.kind == nkSym and sfGenSym in n.sym.flags and n.sym.owner == orig: if n.kind == nkSym and sfGenSym in n.sym.flags and n.sym.owner == orig:
@ -128,17 +112,6 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
proc addProcDecls(c: PContext, fn: PSym) =
# get the proc itself in scope (e.g. for recursion)
addDecl(c, fn)
for i in 1 .. <fn.typ.n.len:
var param = fn.typ.n.sons[i].sym
param.owner = fn
addParamOrResult(c, param, fn.kind)
maybeAddResult(c, fn, fn.ast)
proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
inc c.inGenericInst inc c.inGenericInst
@ -172,9 +145,10 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
popInfoContext() popInfoContext()
proc sideEffectsCheck(c: PContext, s: PSym) = proc sideEffectsCheck(c: PContext, s: PSym) =
if {sfNoSideEffect, sfSideEffect} * s.flags == when false:
{sfNoSideEffect, sfSideEffect}: if {sfNoSideEffect, sfSideEffect} * s.flags ==
localError(s.info, errXhasSideEffects, s.name.s) {sfNoSideEffect, sfSideEffect}:
localError(s.info, errXhasSideEffects, s.name.s)
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
allowMetaTypes = false): PType = allowMetaTypes = false): PType =
@ -187,9 +161,6 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
cl.allowMetaTypes = allowMetaTypes cl.allowMetaTypes = allowMetaTypes
result = replaceTypeVarsT(cl, header) result = replaceTypeVarsT(cl, header)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
result = instGenericContainer(c, n.info, header)
proc instantiateProcType(c: PContext, pt: TIdTable, proc instantiateProcType(c: PContext, pt: TIdTable,
prc: PSym, info: TLineInfo) = prc: PSym, info: TLineInfo) =
# XXX: Instantiates a generic proc signature, while at the same # XXX: Instantiates a generic proc signature, while at the same

View file

@ -151,13 +151,6 @@ proc isStrangeArray(t: PType): bool =
let t = t.skipTypes(abstractInst) let t = t.skipTypes(abstractInst)
result = t.kind == tyArray and t.firstOrd != 0 result = t.kind == tyArray and t.firstOrd != 0
proc isNegative(n: PNode): bool =
let n = n.skipConv
if n.kind in {nkCharLit..nkUInt64Lit}:
result = n.intVal < 0
elif n.kind in nkCallKinds and n.sons[0].kind == nkSym:
result = n.sons[0].sym.magic in {mUnaryMinusI, mUnaryMinusI64}
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
case n[0].sym.magic case n[0].sym.magic

View file

@ -59,7 +59,7 @@ type
init: seq[int] # list of initialized variables init: seq[int] # list of initialized variables
guards: TModel # nested guards guards: TModel # nested guards
locked: seq[PNode] # locked locations locked: seq[PNode] # locked locations
gcUnsafe, isRecursive, isToplevel: bool gcUnsafe, isRecursive, isToplevel, hasSideEffect: bool
maxLockLevel, currLockLevel: TLockLevel maxLockLevel, currLockLevel: TLockLevel
PEffects = var TEffects PEffects = var TEffects
@ -131,7 +131,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if field != nil: break if field != nil: break
ty = ty.sons[0] ty = ty.sons[0]
if ty == nil: break if ty == nil: break
ty = ty.skipTypes(abstractPtrs) ty = ty.skipTypes(skipPtrs)
if field == nil: if field == nil:
localError(n.info, errGenerated, "invalid guard field: " & g.name.s) localError(n.info, errGenerated, "invalid guard field: " & g.name.s)
return return
@ -192,6 +192,14 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
a.owner, reason.info) a.owner, reason.info)
when true:
template markSideEffect(a: PEffects; reason: typed) =
a.hasSideEffect = true
else:
template markSideEffect(a: PEffects; reason: typed) =
a.hasSideEffect = true
markGcUnsafe(a, reason)
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) = proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
let u = s.gcUnsafetyReason let u = s.gcUnsafetyReason
if u != nil and not cycleCheck.containsOrIncl(u.id): if u != nil and not cycleCheck.containsOrIncl(u.id):
@ -226,12 +234,16 @@ proc useVar(a: PEffects, n: PNode) =
message(n.info, warnUninit, s.name.s) message(n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable: # prevent superfluous warnings about the same variable:
a.init.add s.id a.init.add s.id
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet}: if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
s.magic != mNimVm:
if s.guard != nil: guardGlobal(a, n, s.guard) if s.guard != nil: guardGlobal(a, n, s.guard)
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(a, s) markGcUnsafe(a, s)
else:
markSideEffect(a, s)
type type
TIntersection = seq[tuple[id, count: int]] # a simple count table TIntersection = seq[tuple[id, count: int]] # a simple count table
@ -268,10 +280,6 @@ proc createTag(n: PNode): PNode =
result.typ = sysTypeFromName"TEffect" result.typ = sysTypeFromName"TEffect"
if not n.isNil: result.info = n.info if not n.isNil: result.info = n.info
proc createAnyGlobal(n: PNode): PNode =
result = newSymNode(anyGlobal)
result.info = n.info
proc addEffect(a: PEffects, e: PNode, useLineInfo=true) = proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
assert e.kind != nkRaiseStmt assert e.kind != nkRaiseStmt
var aa = a.exc var aa = a.exc
@ -495,6 +503,8 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
if notGcSafe(s.typ) and sfImportc notin s.flags: if notGcSafe(s.typ) and sfImportc notin s.flags:
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked, s) markGcUnsafe(tracked, s)
if tfNoSideEffect notin s.typ.flags:
markSideEffect(tracked, s)
mergeLockLevels(tracked, n, s.getLockLevel) mergeLockLevels(tracked, n, s.getLockLevel)
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@ -550,12 +560,16 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked, a) markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
markSideEffect(tracked, a)
else: else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op): if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked, a) markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags:
markSideEffect(tracked, a)
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
@ -684,6 +698,7 @@ proc track(tracked: PEffects, n: PNode) =
if a.sym == tracked.owner: tracked.isRecursive = true if a.sym == tracked.owner: tracked.isRecursive = true
# even for recursive calls we need to check the lock levels (!): # even for recursive calls we need to check the lock levels (!):
mergeLockLevels(tracked, n, a.sym.getLockLevel) mergeLockLevels(tracked, n, a.sym.getLockLevel)
if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
else: else:
mergeLockLevels(tracked, n, op.lockLevel) mergeLockLevels(tracked, n, op.lockLevel)
var effectList = op.n.sons[0] var effectList = op.n.sons[0]
@ -702,6 +717,10 @@ proc track(tracked: PEffects, n: PNode) =
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(tracked, a) markGcUnsafe(tracked, a)
if tfNoSideEffect notin op.flags and not importedFromC(a):
# and it's not a recursive call:
if not (a.kind == nkSym and a.sym == tracked.owner):
markSideEffect(tracked, a)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
@ -793,9 +812,6 @@ proc track(tracked: PEffects, n: PNode) =
proc subtypeRelation(spec, real: PNode): bool = proc subtypeRelation(spec, real: PNode): bool =
result = safeInheritanceDiff(real.excType, spec.typ) <= 0 result = safeInheritanceDiff(real.excType, spec.typ) <= 0
proc symbolPredicate(spec, real: PNode): bool =
result = real.sym.id == spec.sym.id
proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool; proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
effectPredicate: proc (a, b: PNode): bool {.nimcall.}) = effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
# check that any real exception is listed in 'spec'; mark those as used; # check that any real exception is listed in 'spec'; mark those as used;
@ -912,8 +928,15 @@ proc trackProc*(s: PSym, body: PNode) =
else: else:
listGcUnsafety(s, onlyWarning=true) listGcUnsafety(s, onlyWarning=true)
#localError(s.info, warnGcUnsafe2, s.name.s) #localError(s.info, warnGcUnsafe2, s.name.s)
if sfNoSideEffect in s.flags and t.hasSideEffect:
when false:
listGcUnsafety(s, onlyWarning=false)
else:
localError(s.info, errXhasSideEffects, s.name.s)
if not t.gcUnsafe: if not t.gcUnsafe:
s.typ.flags.incl tfGcSafe s.typ.flags.incl tfGcSafe
if not t.hasSideEffect and sfSideEffect notin s.flags:
s.typ.flags.incl tfNoSideEffect
if s.typ.lockLevel == UnspecifiedLockLevel: if s.typ.lockLevel == UnspecifiedLockLevel:
s.typ.lockLevel = t.maxLockLevel s.typ.lockLevel = t.maxLockLevel
elif t.maxLockLevel > s.typ.lockLevel: elif t.maxLockLevel > s.typ.lockLevel:

View file

@ -155,7 +155,10 @@ proc discardCheck(c: PContext, result: PNode) =
else: else:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
localError(n.info, errDiscardValueX, result.typ.typeToString) if result.typ.kind == tyProc:
localError(n.info, "value of type '" & result.typ.typeToString & "' has to be discarded; for a function call use ()")
else:
localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode = proc semIf(c: PContext, n: PNode): PNode =
result = n result = n
@ -659,7 +662,7 @@ proc semRaise(c: PContext, n: PNode): PNode =
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) n.sons[0] = semExprWithType(c, n.sons[0])
var typ = n.sons[0].typ var typ = n.sons[0].typ
if typ.kind != tyRef or typ.sons[0].kind != tyObject: if typ.kind != tyRef or typ.lastSon.kind != tyObject:
localError(n.info, errExprCannotBeRaised) localError(n.info, errExprCannotBeRaised)
proc addGenericParamListToScope(c: PContext, n: PNode) = proc addGenericParamListToScope(c: PContext, n: PNode) =
@ -1141,11 +1144,6 @@ type
TProcCompilationSteps = enum TProcCompilationSteps = enum
stepRegisterSymbol, stepRegisterSymbol,
stepDetermineType, stepDetermineType,
stepCompileBody
proc isForwardDecl(s: PSym): bool =
internalAssert s.kind == skProc
result = s.ast[bodyPos].kind != nkEmpty
proc semProcAux(c: PContext, n: PNode, kind: TSymKind, proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
validPragmas: TSpecialWords, validPragmas: TSpecialWords,
@ -1183,8 +1181,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
s.ast = n s.ast = n
#s.scope = c.currentScope #s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope # before compiling the proc body, set as current the scope
# where the proc was declared # where the proc was declared
let oldScope = c.currentScope let oldScope = c.currentScope
@ -1234,7 +1230,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
implicitPragmas(c, s, n, validPragmas) implicitPragmas(c, s, n, validPragmas)
else: else:
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc) localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX,
"'" & proto.name.s & "' from " & $proto.info)
if sfForward notin proto.flags: if sfForward notin proto.flags:
wrongRedefinition(n.info, proto.name.s) wrongRedefinition(n.info, proto.name.s)
excl(proto.flags, sfForward) excl(proto.flags, sfForward)
@ -1403,6 +1400,11 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
if namePos >= result.safeLen: return result if namePos >= result.safeLen: return result
var s = result.sons[namePos].sym var s = result.sons[namePos].sym
var t = s.typ var t = s.typ
var allUntyped = true
for i in 1 .. t.n.len-1:
let param = t.n.sons[i].sym
if param.typ.kind != tyExpr: allUntyped = false
if allUntyped: incl(s.flags, sfAllUntyped)
if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro")
if n.sons[bodyPos].kind == nkEmpty: if n.sons[bodyPos].kind == nkEmpty:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
@ -1566,8 +1568,3 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semStmt(c: PContext, n: PNode): PNode = proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias: # now: simply an alias:
result = semExprNoType(c, n) result = semExprNoType(c, n)
proc semStmtScope(c: PContext, n: PNode): PNode =
openScope(c)
result = semStmt(c, n)
closeScope(c)

View file

@ -287,35 +287,6 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
for i in 0.. < n.len: for i in 0.. < n.len:
result.sons[i] = semTemplBody(c, n.sons[i]) result.sons[i] = semTemplBody(c, n.sons[i])
proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode =
let ident = getIdent(opr)
if ident.id in c.toInject: return n
let s = searchInScopes(c.c, ident)
if s != nil:
var callee: PNode
if contains(c.toBind, s.id):
callee = symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id):
callee = symChoice(c.c, n, s, scForceOpen)
elif s.owner == c.owner and sfGenSym in s.flags:
# template tmp[T](x: var seq[T]) =
# var yz: T
incl(s.flags, sfUsed)
callee = newSymNode(s, n.info)
styleCheckUse(n.info, s)
else:
callee = semTemplSymbol(c.c, n, s)
let call = newNodeI(nkCall, n.info)
call.add(callee)
for i in 0 .. n.len-1: call.add(n[i])
result = newNodeI(nkBind, n.info, 2)
result.sons[0] = n
result.sons[1] = call
else:
result = n
proc oprIsRoof(n: PNode): bool = proc oprIsRoof(n: PNode): bool =
const roof = "^" const roof = "^"
case n.kind case n.kind

View file

@ -110,6 +110,8 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string,
result = newOrPrevType(kind, prev, c) result = newOrPrevType(kind, prev, c)
if sonsLen(n) == 2: if sonsLen(n) == 2:
var base = semTypeNode(c, n.sons[1], nil) var base = semTypeNode(c, n.sons[1], nil)
if base.kind == tyVoid:
localError(n.info, errTIsNotAConcreteType, typeToString(base))
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
localError(n.info, errXExpectsOneTypeParam, kindStr) localError(n.info, errXExpectsOneTypeParam, kindStr)
@ -659,11 +661,12 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyObject) if n.sonsLen == 0: return newConstraint(c, tyObject)
var check = initIntSet() var check = initIntSet()
var pos = 0 var pos = 0
var base: PType = nil var base, realBase: PType = nil
# n.sons[0] contains the pragmas (if any). We process these later... # n.sons[0] contains the pragmas (if any). We process these later...
checkSonsLen(n, 3) checkSonsLen(n, 3)
if n.sons[1].kind != nkEmpty: if n.sons[1].kind != nkEmpty:
base = skipTypesOrNil(semTypeNode(c, n.sons[1].sons[0], nil), skipPtrs) realBase = semTypeNode(c, n.sons[1].sons[0], nil)
base = skipTypesOrNil(realBase, skipPtrs)
if base.isNil: if base.isNil:
localError(n.info, errIllegalRecursionInTypeX, "object") localError(n.info, errIllegalRecursionInTypeX, "object")
else: else:
@ -674,9 +677,10 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if concreteBase.kind != tyError: if concreteBase.kind != tyError:
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
base = nil base = nil
realBase = nil
if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") if n.kind != nkObjectTy: internalError(n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c) result = newOrPrevType(tyObject, prev, c)
rawAddSon(result, base) rawAddSon(result, realBase)
if result.n.isNil: if result.n.isNil:
result.n = newNodeI(nkRecList, n.info) result.n = newNodeI(nkRecList, n.info)
else: else:
@ -1137,14 +1141,12 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
checkSonsLen(n, 2) checkSonsLen(n, 2)
openScope(c) openScope(c)
result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true) result = semProcTypeNode(c, n.sons[0], nil, prev, kind, isType=true)
# start with 'ccClosure', but of course pragmas can overwrite this:
result.callConv = ccClosure
# dummy symbol for `pragma`: # dummy symbol for `pragma`:
var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c) var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c)
s.typ = result s.typ = result
if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: if n.sons[1].kind != nkEmpty and n.sons[1].len > 0:
if result.callConv == ccDefault:
result.callConv = ccClosure
#Message(n.info, warnImplicitClosure, renderTree(n))
else:
pragma(c, s, n.sons[1], procTypePragmas) pragma(c, s, n.sons[1], procTypePragmas)
when useEffectSystem: setEffectsForProcType(result, n.sons[1]) when useEffectSystem: setEffectsForProcType(result, n.sons[1])
closeScope(c) closeScope(c)

View file

@ -86,7 +86,7 @@ type
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode
template checkMetaInvariants(cl: TReplTypeVars, t: PType) = template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
when false: when false:
@ -151,7 +151,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
return n return n
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
if n == nil: return if n == nil: return
result = copyNode(n) result = copyNode(n)
if n.typ != nil: if n.typ != nil:
@ -195,7 +195,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
var length = sonsLen(n) var length = sonsLen(n)
if length > 0: if length > 0:
newSons(result, length) newSons(result, length)
for i in countup(0, length - 1): if start > 0:
result.sons[0] = n.sons[0]
for i in countup(start, length - 1):
result.sons[i] = replaceTypeVarsN(cl, n.sons[i]) result.sons[i] = replaceTypeVarsN(cl, n.sons[i])
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
@ -462,8 +464,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
r = skipTypes(r2, {tyPtr, tyRef}) r = skipTypes(r2, {tyPtr, tyRef})
result.sons[i] = r result.sons[i] = r
propagateToOwner(result, r) propagateToOwner(result, r)
# bug #4677: Do not instantiate effect lists
result.n = replaceTypeVarsN(cl, result.n) result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc))
case result.kind case result.kind
of tyArray: of tyArray:

View file

@ -352,18 +352,28 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: result = isIntConv else: result = isIntConv
else: result = isNone else: result = isNone
proc isObjectSubtype(a, f: PType): int = proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a var t = a
assert t.kind == tyObject assert t.kind == tyObject
var depth = 0 var depth = 0
var last = a
while t != nil and not sameObjectTypes(f, t): while t != nil and not sameObjectTypes(f, t):
assert t.kind == tyObject assert t.kind == tyObject
t = t.sons[0] t = t.sons[0]
if t == nil: break if t == nil: break
t = skipTypes(t, {tyGenericInst}) last = t
t = skipTypes(t, skipPtrs)
inc depth inc depth
if t != nil: if t != nil:
if fGenericOrigin != nil and last.kind == tyGenericInst and
last.len-1 == fGenericOrigin.len:
for i in countup(1, sonsLen(fGenericOrigin) - 1):
let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
if x == nil:
put(c, fGenericOrigin.sons[i], last.sons[i])
result = depth result = depth
else:
result = -1
type type
SkippedPtr = enum skippedNone, skippedRef, skippedPtr SkippedPtr = enum skippedNone, skippedRef, skippedPtr
@ -840,12 +850,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
of tyArrayConstr: of tyArray, tyArrayConstr:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype # [] is allowed here
elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
result = isSubtype
of tyArray:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype result = isSubtype
elif typeRel(c, base(f), a.sons[1]) >= isGeneric: elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
@ -896,7 +901,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isEqual result = isEqual
# elif tfHasMeta in f.flags: result = recordRel(c, f, a) # elif tfHasMeta in f.flags: result = recordRel(c, f, a)
else: else:
var depth = isObjectSubtype(a, f) var depth = isObjectSubtype(c, a, f, nil)
if depth > 0: if depth > 0:
inc(c.inheritancePenalty, depth) inc(c.inheritancePenalty, depth)
result = isSubtype result = isSubtype
@ -1012,6 +1017,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isGeneric result = isGeneric
else: else:
let genericBody = f.sons[0] let genericBody = f.sons[0]
var askip = skippedNone
var fskip = skippedNone
let aobj = x.skipToObject(askip)
let fobj = genericBody.lastSon.skipToObject(fskip)
if fobj != nil and aobj != nil and askip == fskip:
let depth = isObjectSubtype(c, aobj, fobj, f)
if depth >= 0:
c.inheritancePenalty += depth
return if depth == 0: isGeneric else: isSubtype
result = typeRel(c, genericBody, x) result = typeRel(c, genericBody, x)
if result != isNone: if result != isNone:
# see tests/generics/tgeneric3.nim for an example that triggers this # see tests/generics/tgeneric3.nim for an example that triggers this
@ -1075,7 +1089,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyBuiltInTypeClass: of tyBuiltInTypeClass:
considerPreviousT: considerPreviousT:
let targetKind = f.sons[0].kind let targetKind = f.sons[0].kind
if targetKind == a.skipTypes({tyRange, tyGenericInst}).kind or if targetKind == a.skipTypes({tyRange, tyGenericInst, tyBuiltInTypeClass}).kind or
(targetKind in {tyProc, tyPointer} and a.kind == tyNil): (targetKind in {tyProc, tyPointer} and a.kind == tyNil):
put(c, f, a) put(c, f, a)
return isGeneric return isGeneric
@ -1310,8 +1324,9 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
var call = newNodeI(nkCall, arg.info) var call = newNodeI(nkCall, arg.info)
call.add(f.n.copyTree) call.add(f.n.copyTree)
call.add(arg.copyTree) call.add(arg.copyTree)
result = c.semOverloadedCall(c, call, call, routineKinds) result = c.semExpr(c, call)
if result != nil: if result != nil:
if result.typ == nil: return nil
# resulting type must be consistent with the other arguments: # resulting type must be consistent with the other arguments:
var r = typeRel(m, f.sons[0], result.typ) var r = typeRel(m, f.sons[0], result.typ)
if r < isGeneric: return nil if r < isGeneric: return nil
@ -1320,13 +1335,6 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
if r == isGeneric: if r == isGeneric:
result.typ = getInstantiatedType(c, arg, m, base(f)) result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true m.baseTypeMatch = true
# bug #4545: allow the call to go through a 'var T':
let vt = result.sons[0].typ.sons[1]
if vt.kind == tyVar:
let x = result.sons[1]
let va = newNodeIT(nkHiddenAddr, x.info, vt)
va.add x
result.sons[1] = va
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
case r case r
@ -1559,8 +1567,13 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
proc setSon(father: PNode, at: int, son: PNode) = proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setLen(father.sons, at + 1) let oldLen = father.len
if oldLen <= at:
setLen(father.sons, at + 1)
father.sons[at] = son father.sons[at] = son
# insert potential 'void' parameters:
#for i in oldLen ..< at:
# father.sons[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
# we are allowed to modify the calling node in the 'prepare*' procs: # we are allowed to modify the calling node in the 'prepare*' procs:
proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
@ -1591,17 +1604,17 @@ proc prepareNamedParam(a: PNode) =
a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
proc arrayConstr(c: PContext, n: PNode): PType = proc arrayConstr(c: PContext, n: PNode): PType =
result = newTypeS(tyArrayConstr, c) result = newTypeS(tyArray, c)
rawAddSon(result, makeRangeType(c, 0, 0, n.info)) rawAddSon(result, makeRangeType(c, 0, 0, n.info))
addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal})) addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal}))
proc arrayConstr(c: PContext, info: TLineInfo): PType = proc arrayConstr(c: PContext, info: TLineInfo): PType =
result = newTypeS(tyArrayConstr, c) result = newTypeS(tyArray, c)
rawAddSon(result, makeRangeType(c, 0, -1, info)) rawAddSon(result, makeRangeType(c, 0, -1, info))
rawAddSon(result, newTypeS(tyEmpty, c)) # needs an empty basetype! rawAddSon(result, newTypeS(tyEmpty, c)) # needs an empty basetype!
proc incrIndexType(t: PType) = proc incrIndexType(t: PType) =
assert t.kind == tyArrayConstr assert t.kind == tyArray
inc t.sons[0].n.sons[1].intVal inc t.sons[0].n.sons[1].intVal
template isVarargsUntyped(x): untyped = template isVarargsUntyped(x): untyped =

View file

@ -273,7 +273,7 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
while true: while true:
suggestObject(c, t.n, outputs) suggestObject(c, t.n, outputs)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], skipPtrs)
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
elif typ.kind == tyTuple and typ.n != nil: elif typ.kind == tyTuple and typ.n != nil:
suggestSymList(c, typ.n, outputs) suggestSymList(c, typ.n, outputs)
@ -413,17 +413,16 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
result = ast.emptyNode result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) = proc suggestExpr*(c: PContext, node: PNode) =
if nfIsCursor notin node.flags: if gTrackPos.line < 0: return
if gTrackPos.line < 0: return var cp = inCheckpoint(node.info)
var cp = inCheckpoint(node.info) if cp == cpNone: return
if cp == cpNone: return
var outputs = 0 var outputs = 0
# This keeps semExpr() from coming here recursively: # This keeps semExpr() from coming here recursively:
if c.compilesContextId > 0: return if c.compilesContextId > 0: return
inc(c.compilesContextId) inc(c.compilesContextId)
if gIdeCmd == ideSug: if gIdeCmd == ideSug:
var n = if nfIsCursor in node.flags: node else: findClosestDot(node) var n = findClosestDot(node)
if n == nil: n = node if n == nil: n = node
if n.kind == nkDotExpr: if n.kind == nkDotExpr:
var obj = safeSemExpr(c, n.sons[0]) var obj = safeSemExpr(c, n.sons[0])
@ -436,7 +435,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
elif gIdeCmd == ideCon: elif gIdeCmd == ideCon:
var n = if nfIsCursor in node.flags: node else: findClosestCall(node) var n = findClosestCall(node)
if n == nil: n = node if n == nil: n = node
if n.kind in nkCallKinds: if n.kind in nkCallKinds:
var a = copyNode(n) var a = copyNode(n)

View file

@ -594,13 +594,6 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
popTransCon(c) popTransCon(c)
# echo "transformed: ", stmtList.PNode.renderTree # echo "transformed: ", stmtList.PNode.renderTree
proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and
call.sons[0].sym.kind in {skProc, skMethod, skConverter}:
result = call.sons[0].sym.magic
else:
result = mNone
proc transformCase(c: PTransf, n: PNode): PTransNode = proc transformCase(c: PTransf, n: PNode): PTransNode =
# removes `elif` branches of a case stmt # removes `elif` branches of a case stmt
# adds ``else: nil`` if needed for the code generator # adds ``else: nil`` if needed for the code generator

View file

@ -12,32 +12,21 @@
import import
ast, astalgo, lexer, msgs, strutils, wordrecg ast, astalgo, lexer, msgs, strutils, wordrecg
proc hasSon(father, son: PNode): bool = proc cyclicTreeAux(n: PNode, visited: var seq[PNode]): bool =
for i in countup(0, sonsLen(father) - 1): if n == nil: return
if father.sons[i] == son: for v in visited:
return true if v == n: return true
result = false
proc cyclicTreeAux(n, s: PNode): bool =
if n == nil:
return false
if hasSon(s, n):
return true
var m = sonsLen(s)
addSon(s, n)
if not (n.kind in {nkEmpty..nkNilLit}): if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1): visited.add(n)
if cyclicTreeAux(n.sons[i], s): for nSon in n.sons:
return true if cyclicTreeAux(nSon, visited): return true
result = false discard visited.pop()
delSon(s, m)
proc cyclicTree*(n: PNode): bool = proc cyclicTree*(n: PNode): bool =
var s = newNodeI(nkEmpty, n.info) var visited: seq[PNode] = @[]
result = cyclicTreeAux(n, s) cyclicTreeAux(n, visited)
proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
result = false
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif (a != nil) and (b != nil) and (a.kind == b.kind):
@ -61,7 +50,6 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
result = true result = true
proc sameTree*(a, b: PNode): bool = proc sameTree*(a, b: PNode): bool =
result = false
if a == b: if a == b:
result = true result = true
elif a != nil and b != nil and a.kind == b.kind: elif a != nil and b != nil and a.kind == b.kind:
@ -84,17 +72,6 @@ proc sameTree*(a, b: PNode): bool =
if not sameTree(a.sons[i], b.sons[i]): return if not sameTree(a.sons[i], b.sons[i]): return
result = true result = true
proc getProcSym*(call: PNode): PSym =
result = call.sons[0].sym
proc getOpSym*(op: PNode): PSym =
if op.kind notin {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}:
result = nil
else:
if sonsLen(op) <= 0: internalError(op.info, "getOpSym")
elif op.sons[0].kind == nkSym: result = op.sons[0].sym
else: result = nil
proc getMagic*(op: PNode): TMagic = proc getMagic*(op: PNode): TMagic =
case op.kind case op.kind
of nkCallKinds: of nkCallKinds:
@ -104,9 +81,8 @@ proc getMagic*(op: PNode): TMagic =
else: result = mNone else: result = mNone
proc isConstExpr*(n: PNode): bool = proc isConstExpr*(n: PNode): bool =
result = (n.kind in const atomKinds = {nkCharLit..nkNilLit} # Char, Int, UInt, Str, Float and Nil literals
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, n.kind in atomKinds or nfAllConst in n.flags
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
proc isCaseObj*(n: PNode): bool = proc isCaseObj*(n: PNode): bool =
if n.kind == nkRecCase: return true if n.kind == nkRecCase: return true
@ -131,25 +107,6 @@ proc isDeepConstExpr*(n: PNode): bool =
result = true result = true
else: discard else: discard
proc flattenTreeAux(d, a: PNode, op: TMagic) =
if (getMagic(a) == op): # a is a "leaf", so add it:
for i in countup(1, sonsLen(a) - 1): # BUGFIX
flattenTreeAux(d, a.sons[i], op)
else:
addSon(d, copyTree(a))
proc flattenTree*(root: PNode, op: TMagic): PNode =
result = copyNode(root)
if getMagic(root) == op:
# BUGFIX: forget to copy prc
addSon(result, copyNode(root.sons[0]))
flattenTreeAux(result, root, op)
proc swapOperands*(op: PNode) =
var tmp = op.sons[1]
op.sons[1] = op.sons[2]
op.sons[2] = tmp
proc isRange*(n: PNode): bool {.inline.} = proc isRange*(n: PNode): bool {.inline.} =
if n.kind in nkCallKinds: if n.kind in nkCallKinds:
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or
@ -168,7 +125,6 @@ proc unnestStmts(n, result: PNode) =
result.add(n) result.add(n)
proc flattenStmts*(n: PNode): PNode = proc flattenStmts*(n: PNode): PNode =
## flattens a nested statement list; used for pattern matching
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
unnestStmts(n, result) unnestStmts(n, result)
if result.len == 1: if result.len == 1:

View file

@ -94,7 +94,8 @@ proc invalidGenericInst(f: PType): bool =
proc isPureObject(typ: PType): bool = proc isPureObject(typ: PType): bool =
var t = typ var t = typ
while t.kind == tyObject and t.sons[0] != nil: t = t.sons[0] while t.kind == tyObject and t.sons[0] != nil:
t = t.sons[0].skipTypes(skipPtrs)
result = t.sym != nil and sfPure in t.sym.flags result = t.sym != nil and sfPure in t.sym.flags
proc getOrdValue(n: PNode): BiggestInt = proc getOrdValue(n: PNode): BiggestInt =
@ -145,10 +146,6 @@ proc elemType*(t: PType): PType =
else: result = t.lastSon else: result = t.lastSon
assert(result != nil) assert(result != nil)
proc skipGeneric(t: PType): PType =
result = t
while result.kind == tyGenericInst: result = lastSon(result)
proc isOrdinalType(t: PType): bool = proc isOrdinalType(t: PType): bool =
assert(t != nil) assert(t != nil)
const const
@ -232,7 +229,8 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
if result: return if result: return
case t.kind case t.kind
of tyObject: of tyObject:
result = searchTypeForAux(t.sons[0], predicate, marker) if t.sons[0] != nil:
result = searchTypeForAux(t.sons[0].skipTypes(skipPtrs), predicate, marker)
if not result: result = searchTypeNodeForAux(t.n, predicate, marker) if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
of tyGenericInst, tyDistinct: of tyGenericInst, tyDistinct:
result = searchTypeForAux(lastSon(t), predicate, marker) result = searchTypeForAux(lastSon(t), predicate, marker)
@ -269,7 +267,9 @@ proc analyseObjectWithTypeFieldAux(t: PType,
if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker): if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
return frEmbedded return frEmbedded
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
res = analyseObjectWithTypeFieldAux(t.sons[i], marker) var x = t.sons[i]
if x != nil: x = x.skipTypes(skipPtrs)
res = analyseObjectWithTypeFieldAux(x, marker)
if res == frEmbedded: if res == frEmbedded:
return frEmbedded return frEmbedded
if res == frHeader: result = frHeader if res == frHeader: result = frHeader
@ -578,10 +578,6 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = typeToStr[t.kind] result = typeToStr[t.kind]
result.addTypeFlags(t) result.addTypeFlags(t)
proc resultType(t: PType): PType =
assert(t.kind == tyProc)
result = t.sons[0] # nil is allowed
proc base(t: PType): PType = proc base(t: PType): PType =
result = t.sons[0] result = t.sons[0]
@ -661,7 +657,14 @@ proc lengthOrd(t: PType): BiggestInt =
case t.kind case t.kind
of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyInt64, tyInt32, tyInt: result = lastOrd(t)
of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0]) of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0])
else: result = lastOrd(t) - firstOrd(t) + 1 else:
let last = lastOrd t
let first = firstOrd t
# XXX use a better overflow check here:
if last == high(BiggestInt) and first <= 0:
result = last
else:
result = lastOrd(t) - firstOrd(t) + 1
# -------------- type equality ----------------------------------------------- # -------------- type equality -----------------------------------------------
@ -772,18 +775,6 @@ proc equalParams(a, b: PNode): TParamsEquality =
result = paramsIncompatible # overloading by different result = paramsIncompatible # overloading by different
# result types does not work # result types does not work
proc sameLiteral(x, y: PNode): bool =
if x.kind == y.kind:
case x.kind
of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal
of nkFloatLit..nkFloat64Lit: result = x.floatVal == y.floatVal
of nkNilLit: result = true
else: assert(false)
proc sameRanges(a, b: PNode): bool =
result = sameLiteral(a.sons[0], b.sons[0]) and
sameLiteral(a.sons[1], b.sons[1])
proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
# two tuples are equivalent iff the names, types and positions are the same; # two tuples are equivalent iff the names, types and positions are the same;
# however, both types may not have any field names (t.n may be nil) which # however, both types may not have any field names (t.n may be nil) which
@ -1070,10 +1061,10 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
of nkNone..nkNilLit: of nkNone..nkNilLit:
discard discard
else: else:
if n.kind == nkRecCase and kind in {skProc, skConst}:
return n[0].typ
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
if it.kind == nkRecCase and kind in {skProc, skConst}:
return n.typ
result = typeAllowedNode(marker, it, kind, flags) result = typeAllowedNode(marker, it, kind, flags)
if result != nil: break if result != nil: break
@ -1306,7 +1297,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
a = maxAlign a = maxAlign
of tyObject: of tyObject:
if typ.sons[0] != nil: if typ.sons[0] != nil:
result = computeSizeAux(typ.sons[0], a) result = computeSizeAux(typ.sons[0].skipTypes(skipPtrs), a)
if result < 0: return if result < 0: return
maxAlign = a maxAlign = a
elif isObjectWithTypeFieldPredicate(typ): elif isObjectWithTypeFieldPredicate(typ):

View file

@ -19,20 +19,14 @@ proc renderPlainSymbolName*(n: PNode): string =
## for the HTML hyperlinks. ## for the HTML hyperlinks.
result = "" result = ""
case n.kind case n.kind
of nkPostfix: of nkPostfix, nkAccQuoted:
for i in 0 .. <n.len: result = renderPlainSymbolName(n[<n.len])
result = renderPlainSymbolName(n[<n.len])
if result.len > 0:
return
of nkIdent: of nkIdent:
if n.ident.s != "*": result = n.ident.s
result = n.ident.s
of nkSym: of nkSym:
result = n.sym.renderDefinitionName(noQuotes = true) result = n.sym.renderDefinitionName(noQuotes = true)
of nkPragmaExpr: of nkPragmaExpr:
result = renderPlainSymbolName(n[0]) result = renderPlainSymbolName(n[0])
of nkAccQuoted:
result = renderPlainSymbolName(n[<n.len])
else: else:
internalError(n.info, "renderPlainSymbolName() with " & $n.kind) internalError(n.info, "renderPlainSymbolName() with " & $n.kind)
assert(not result.isNil) assert(not result.isNil)

View file

@ -171,6 +171,7 @@ proc copyValue(src: PNode): PNode =
result.info = src.info result.info = src.info
result.typ = src.typ result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds): when defined(useNodeIds):
if result.id == nodeIdToDebug: if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
@ -269,6 +270,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame):
c.currentExceptionA = nil c.currentExceptionA = nil
# execute the corresponding handler: # execute the corresponding handler:
while c.code[pc2].opcode == opcExcept: inc pc2 while c.code[pc2].opcode == opcExcept: inc pc2
discard f.safePoints.pop
return (pc2, f) return (pc2, f)
inc pc2 inc pc2
if c.code[pc2].opcode != opcExcept and nextExceptOrFinally >= 0: if c.code[pc2].opcode != opcExcept and nextExceptOrFinally >= 0:
@ -282,7 +284,8 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame):
pc2 = nextExceptOrFinally pc2 = nextExceptOrFinally
if c.code[pc2].opcode == opcFinally: if c.code[pc2].opcode == opcFinally:
# execute the corresponding handler, but don't quit walking the stack: # execute the corresponding handler, but don't quit walking the stack:
return (pc2, f) discard f.safePoints.pop
return (pc2+1, f)
# not the right one: # not the right one:
discard f.safePoints.pop discard f.safePoints.pop
@ -964,7 +967,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# we'll execute in the 'raise' handler # we'll execute in the 'raise' handler
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
inc pc, rbx inc pc, rbx
assert c.code[pc+1].opcode in {opcExcept, opcFinally} while c.code[pc+1].opcode == opcExcept:
let rbx = c.code[pc+1].regBx - wordExcess - 1
inc pc, rbx
#assert c.code[pc+1].opcode in {opcExcept, opcFinally}
if c.code[pc+1].opcode != opcFinally:
# in an except handler there is no active safe point for the 'try':
tos.popSafePoint()
of opcFinally: of opcFinally:
# just skip it; it's followed by the code we need to execute anyway # just skip it; it's followed by the code we need to execute anyway
tos.popSafePoint() tos.popSafePoint()
@ -1063,7 +1072,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcRepr: of opcRepr:
decodeB(rkNode) decodeB(rkNode)
createStr regs[ra] createStr regs[ra]
regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments}) regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments})
of opcQuit: of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}: if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled) message(c.debug[pc], hintQuitCalled)

View file

@ -109,10 +109,6 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
mapTypeToBracketX(name, m, t, info, inst) mapTypeToBracketX(name, m, t, info, inst)
template newNodeX(kind): untyped = template newNodeX(kind): untyped =
newNodeIT(kind, if t.n.isNil: info else: t.n.info, t) newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
template newIdent(s): untyped =
var r = newNodeX(nkIdent)
r.add !s
r
template newIdentDefs(n,t): untyped = template newIdentDefs(n,t): untyped =
var id = newNodeX(nkIdentDefs) var id = newNodeX(nkIdentDefs)
id.add n # name id.add n # name

View file

@ -748,7 +748,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
let tmp = c.genx(arg) let tmp = c.genx(arg)
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, tmp) c.gABC(n, opc, dest, tmp)
c.gABx(n, opc, 0, genType(c, n.typ)) c.gABx(n, opc, 0, genType(c, n.typ.skipTypes({tyStatic})))
c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic}))) c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
c.freeTemp(tmp) c.freeTemp(tmp)
@ -1127,14 +1127,6 @@ proc fitsRegister*(t: PType): bool =
t.skipTypes(abstractInst-{tyTypeDesc}).kind in { t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
proc requiresCopy(n: PNode): bool =
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
result = false
elif n.kind in ({nkCurly, nkBracket, nkPar, nkObjConstr}+nkCallKinds):
result = false
else:
result = true
proc unneededIndirection(n: PNode): bool = proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
@ -1215,8 +1207,6 @@ proc whichAsgnOpc(n: PNode): TOpcode =
else: else:
opcAsgnComplex opcAsgnComplex
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
@ -1268,9 +1258,6 @@ proc isTemp(c: PCtx; dest: TDest): bool =
template needsAdditionalCopy(n): untyped = template needsAdditionalCopy(n): untyped =
not c.isTemp(dest) and not fitsRegister(n.typ) not c.isTemp(dest) and not fitsRegister(n.typ)
proc skipDeref(n: PNode): PNode =
result = if n.kind in {nkDerefExpr, nkHiddenDeref}: n.sons[0] else: n
proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
dest, idx, value: TRegister) = dest, idx, value: TRegister) =
# opcLdObj et al really means "load address". We sometimes have to create a # opcLdObj et al really means "load address". We sometimes have to create a
@ -1848,7 +1835,6 @@ proc genParams(c: PCtx; params: PNode) =
# res.sym.position is already 0 # res.sym.position is already 0
c.prc.slots[0] = (inUse: true, kind: slotFixedVar) c.prc.slots[0] = (inUse: true, kind: slotFixedVar)
for i in 1.. <params.len: for i in 1.. <params.len:
let param = params.sons[i].sym
c.prc.slots[i] = (inUse: true, kind: slotFixedLet) c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
c.prc.maxSlots = max(params.len, 1) c.prc.maxSlots = max(params.len, 1)

View file

@ -66,7 +66,7 @@ path="$lib/pure"
@end @end
@if unix: @if unix:
@if not bsd: @if not bsd or haiku:
# -fopenmp # -fopenmp
gcc.options.linker = "-ldl" gcc.options.linker = "-ldl"
gcc.cpp.options.linker = "-ldl" gcc.cpp.options.linker = "-ldl"
@ -80,6 +80,14 @@ path="$lib/pure"
# at least NetBSD has problems with thread local storage: # at least NetBSD has problems with thread local storage:
tlsEmulation:on tlsEmulation:on
@end @end
@if haiku:
# -fopenmp
gcc.options.linker = "-lroot -lnetwork"
gcc.cpp.options.linker = "-lroot -lnetwork"
clang.options.linker = "-lroot -lnetwork"
clang.cpp.options.linker = "-lroot -lnetwork"
tcc.options.linker = "-lroot -lnetwork"
@end
@end @end
# Configuration for the Intel C/C++ compiler: # Configuration for the Intel C/C++ compiler:
@ -122,6 +130,17 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
clang.objc.options.linker = "-framework Foundation" clang.objc.options.linker = "-framework Foundation"
@end @end
# Options for FreeBSD, OpenBSD, NetBSD linker to add locations for searching
# shared libraries.
@if freebsd or openbsd or netbsd:
gcc.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
gcc.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
llvm_gcc.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
llvm_gcc.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
clang.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
clang.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib"
@end
# Configuration for the VxWorks # Configuration for the VxWorks
# This has been tested with VxWorks 6.9 only # This has been tested with VxWorks 6.9 only
@if vxworks: @if vxworks:

View file

@ -49,7 +49,7 @@ $seeSrc
# See doc.item for available substitution variables. # See doc.item for available substitution variables.
doc.item.toc = """ doc.item.toc = """
<li><a class="reference" href="#$itemSymOrID" <li><a class="reference" href="#$itemSymOrID"
title="$header_plain">$name</a></li> title="$header_plain">$name<span class="attachedType" style="visibility:hidden">$attype</span></a></li>
""" """
# HTML rendered for doc.item's seeSrc variable. Note that this will render to # HTML rendered for doc.item's seeSrc variable. Note that this will render to
@ -60,8 +60,10 @@ doc.item.toc = """
# * $url: whatever you did pass through the --docSeeSrcUrl switch (which also # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also
# gets variables path/line replaced!) # gets variables path/line replaced!)
doc.item.seesrc = """&nbsp;&nbsp;<a doc.item.seesrc = """&nbsp;&nbsp;<a
href="${url}/${path}#L${line}" href="${url}/tree/${commit}/${path}#L${line}"
class="link-seesrc" target="_blank">Source</a>""" class="link-seesrc" target="_blank">Source</a>
<a href="${url}/edit/devel/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>
"""
doc.toc = """ doc.toc = """
<ul class="simple simple-toc" id="toc-list"> <ul class="simple simple-toc" id="toc-list">
@ -72,9 +74,38 @@ $content
doc.body_toc = """ doc.body_toc = """
<div class="row"> <div class="row">
<div class="three columns"> <div class="three columns">
<div>
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
$tableofcontents $tableofcontents
</div> </div>
<div class="nine columns" id="content"> <div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc">$moduledesc</p>
$content
</div>
</div>
"""
doc.body_toc_group = """
<div class="row">
<div class="three columns">
<div>
Search: <input type="text" id="searchInput"
onkeyup="search()" />
</div>
<div>
Group by:
<select onchange="groupBy(this.value)">
<option value="section">Section</option>
<option value="type">Type</option>
</select>
</div>
$tableofcontents
</div>
<div class="nine columns" id="content">
<div id="tocRoot"></div>
<p class="module-desc">$moduledesc</p> <p class="module-desc">$moduledesc</p>
$content $content
</div> </div>
@ -93,7 +124,7 @@ doc.listing_end = "</pre>"
doc.file = """<?xml version="1.0" encoding="utf-8" ?> doc.file = """<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nimrod. --> <!-- This file is generated by Nim. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
@ -1244,8 +1275,16 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
span.pragmaend { span.pragmaend {
cursor: pointer; cursor: pointer;
} }
div.search_results {
background-color: antiquewhite;
margin: 3em;
padding: 1em;
border: 1px solid #4d4d4d;
}
</style> </style>
<script type="text/javascript" src="../dochack.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function togglepragma(d) { function togglepragma(d) {

View file

@ -3,6 +3,7 @@ Advanced commands:
//compileToCpp, cpp compile project to C++ code //compileToCpp, cpp compile project to C++ code
//compileToOC, objc compile project to Objective C code //compileToOC, objc compile project to Objective C code
//js compile project to Javascript //js compile project to Javascript
//e run a Nimscript file
//rst2html convert a reStructuredText file to HTML //rst2html convert a reStructuredText file to HTML
//rst2tex convert a reStructuredText file to TeX //rst2tex convert a reStructuredText file to TeX
//jsondoc extract the documentation to a json file //jsondoc extract the documentation to a json file

View file

@ -582,4 +582,4 @@ Nim programming language.
nimblepkglist.js or have javascript disabled in your browser.</b></div> nimblepkglist.js or have javascript disabled in your browser.</b></div>
<script type="text/javascript" src="nimblepkglist.js"></script> <script type="text/javascript" src="nimblepkglist.js"></script>
<script type="text/javascript" src="http://irclogs.nim-lang.org/packages?callback=gotPackageList"></script> <script type="text/javascript" src="http://irclogs.nim-lang.org/packages?callback=gotPackageList" async></script>

View file

@ -28,7 +28,7 @@ The parser uses a stack of indentation levels: the stack consists of integers
counting the spaces. The indentation information is queried at strategic counting the spaces. The indentation information is queried at strategic
places in the parser but ignored otherwise: The pseudo terminal ``IND{>}`` places in the parser but ignored otherwise: The pseudo terminal ``IND{>}``
denotes an indentation that consists of more spaces than the entry at the top denotes an indentation that consists of more spaces than the entry at the top
of the stack; IND{=} an indentation that has the same number of spaces. ``DED`` of the stack; ``IND{=}`` an indentation that has the same number of spaces. ``DED``
is another pseudo terminal that describes the *action* of popping a value is another pseudo terminal that describes the *action* of popping a value
from the stack, ``IND{>}`` then implies to push onto the stack. from the stack, ``IND{>}`` then implies to push onto the stack.
@ -399,7 +399,7 @@ These keywords are also operators:
`=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they
are used for other notational purposes. are used for other notational purposes.
``*:`` is as a special case the two tokens `*`:tok: and `:`:tok: ``*:`` is as a special case treated as the two tokens `*`:tok: and `:`:tok:
(to support ``var v*: T``). (to support ``var v*: T``).

View file

@ -161,7 +161,7 @@ requires full qualification.
asmNoStackFrame pragma asmNoStackFrame pragma
---------------------- ----------------------
A proc can be marked with the ``AsmNoStackFrame`` pragma to tell the compiler A proc can be marked with the ``asmNoStackFrame`` pragma to tell the compiler
it should not generate a stack frame for the proc. There are also no exit it should not generate a stack frame for the proc. There are also no exit
statements like ``return result;`` generated and the generated C function is statements like ``return result;`` generated and the generated C function is
declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on

View file

@ -61,14 +61,14 @@ Calling a procedure can be done in many different ways:
.. code-block:: nim .. code-block:: nim
proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ... proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
# call with positional arguments # parameter bindings: # call with positional arguments # parameter bindings:
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true) callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
# call with named and positional arguments: # call with named and positional arguments:
callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false) callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
# call with named arguments (order is not relevant): # call with named arguments (order is not relevant):
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false) callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
# call as a command statement: no () needed: # call as a command statement: no () needed:
callme 0, 1, "abc", '\t' callme 0, 1, "abc", '\t' # (x=0, y=1, s="abc", c='\t', b=false)
A procedure may call itself recursively. A procedure may call itself recursively.

View file

@ -462,10 +462,10 @@ While statement
Example: Example:
.. code-block:: nim .. code-block:: nim
echo "Please tell me your password: \n" echo "Please tell me your password:"
var pw = readLine(stdin) var pw = readLine(stdin)
while pw != "12345": while pw != "12345":
echo "Wrong password! Next try: \n" echo "Wrong password! Next try:"
pw = readLine(stdin) pw = readLine(stdin)

View file

@ -62,7 +62,8 @@ Precedence level Operators First charact
================ =============================================== ================== =============== ================ =============================================== ================== ===============
Whether an operator is used a prefix operator is also affected by preceeding whitespace (this parsing change was introduced with version 0.13.0): Whether an operator is used a prefix operator is also affected by preceding
whitespace (this parsing change was introduced with version 0.13.0):
.. code-block:: nim .. code-block:: nim
echo $foo echo $foo

View file

@ -406,11 +406,11 @@ builtin can be used for that:
macro debug(n: varargs[typed]): untyped = macro debug(n: varargs[typed]): untyped =
result = newNimNode(nnkStmtList, n) result = newNimNode(nnkStmtList, n)
for i in 0..n.len-1: for x in n:
# we can bind symbols in scope via 'bindSym': # we can bind symbols in scope via 'bindSym':
add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(n[i]))) add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(x)))
add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": "))) add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": ")))
add(result, newCall(bindSym"writeLine", bindSym"stdout", n[i])) add(result, newCall(bindSym"writeLine", bindSym"stdout", x))
var var
a: array [0..10, int] a: array [0..10, int]

View file

@ -13,7 +13,7 @@ Example:
Sym = object # a symbol Sym = object # a symbol
name: string # the symbol's name name: string # the symbol's name
line: int # the line the symbol was declared in line: int # the line the symbol was declared in
code: Node # the symbol's abstract syntax tree code: Node # the symbol's abstract syntax tree
A type section begins with the ``type`` keyword. It contains multiple A type section begins with the ``type`` keyword. It contains multiple
type definitions. A type definition binds a type to a name. Type definitions type definitions. A type definition binds a type to a name. Type definitions

View file

@ -227,8 +227,8 @@ floating pointer values at compile time; this means expressions like
Boolean type Boolean type
------------ ------------
The boolean type is named `bool`:idx: in Nim and can be one of the two The boolean type is named `bool`:idx: in Nim and can be one of the two
pre-defined values ``true`` and ``false``. Conditions in while, pre-defined values ``true`` and ``false``. Conditions in ``while``,
if, elif, when statements need to be of type bool. ``if``, ``elif``, ``when``-statements need to be of type ``bool``.
This condition holds:: This condition holds::
@ -390,7 +390,10 @@ i-th *unichar*. The iterator ``runes`` from the `unicode module
cstring type cstring type
------------ ------------
The ``cstring`` type represents a pointer to a zero-terminated char array
The ``cstring`` type meaning `compatible string` is the native representation
of a string for the compilation backend. For the C backend the ``cstring`` type
represents a pointer to a zero-terminated char array
compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy
interfacing with C. The index operation ``s[i]`` means the i-th *char* of interfacing with C. The index operation ``s[i]`` means the i-th *char* of
``s``; however no bounds checking for ``cstring`` is performed making the ``s``; however no bounds checking for ``cstring`` is performed making the
@ -421,7 +424,6 @@ string from a cstring:
var cstr: cstring = str var cstr: cstring = str
var newstr: string = $cstr var newstr: string = $cstr
Structured types Structured types
---------------- ----------------
A variable of a structured type can hold multiple values at the same A variable of a structured type can hold multiple values at the same
@ -607,7 +609,7 @@ is similar to the ``instanceof`` operator in Java.
age: int # no * means that the field is hidden age: int # no * means that the field is hidden
Student = ref object of Person # a student is a person Student = ref object of Person # a student is a person
id: int # with an id field id: int # with an id field
var var
student: Student student: Student
@ -1260,4 +1262,4 @@ Is the same as:
However later versions of the language might change this to mean "infer the However later versions of the language might change this to mean "infer the
parameters' types from the body". Then the above ``foo`` would be rejected as parameters' types from the body". Then the above ``foo`` would be rejected as
the parameters' types can not be infered from an empty ``discard`` statement. the parameters' types can not be inferred from an empty ``discard`` statement.

View file

@ -80,7 +80,7 @@ a location. A query location consists of:
``col`` ``col``
An integer with the column you are going to query. For the An integer with the column you are going to query. For the
compiler columns start at **1**. compiler columns start at **0**.
Definitions Definitions
@ -160,7 +160,7 @@ tab characters (``\t``). The values of each column are:
6. Line where the symbol is located in the file. Lines start to 6. Line where the symbol is located in the file. Lines start to
count at **1**. count at **1**.
7. Column where the symbol is located in the file. Columns start 7. Column where the symbol is located in the file. Columns start
to count at **1**. to count at **0**.
8. Docstring for the symbol if available or the empty string. To 8. Docstring for the symbol if available or the empty string. To
differentiate the docstring from end of answer, differentiate the docstring from end of answer,
the docstring is always provided enclosed in double quotes, and the docstring is always provided enclosed in double quotes, and

View file

@ -1,5 +1,5 @@
The set type models the mathematical notion of a set. The set's The set type models the mathematical notion of a set. The set's basetype can
basetype can only be an ordinal type of a certain size, namely: only be an ordinal type of a certain size, namely:
* ``int8``-``int16`` * ``int8``-``int16``
* ``uint8``/``byte``-``uint16`` * ``uint8``/``byte``-``uint16``
* ``char`` * ``char``

View file

@ -674,17 +674,18 @@ variable number of arguments:
macro debug(n: varargs[expr]): stmt = macro debug(n: varargs[expr]): stmt =
# `n` is a Nim AST that contains a list of expressions; # `n` is a Nim AST that contains a list of expressions;
# this macro returns a list of statements: # this macro returns a list of statements (n is passed for proper line
# information):
result = newNimNode(nnkStmtList, n) result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro: # iterate over any argument that is passed to this macro:
for i in 0..n.len-1: for x in n:
# add a call to the statement list that writes the expression; # add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation: # `toStrLit` converts an AST to its string representation:
result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) result.add(newCall("write", newIdentNode("stdout"), toStrLit(x)))
# add a call to the statement list that writes ": " # add a call to the statement list that writes ": "
result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
# add a call to the statement list that writes the expressions value: # add a call to the statement list that writes the expressions value:
result.add(newCall("writeLine", newIdentNode("stdout"), n[i])) result.add(newCall("writeLine", newIdentNode("stdout"), x))
var var
a: array[0..10, int] a: array[0..10, int]

158
finish.nim Normal file
View file

@ -0,0 +1,158 @@
# -------------- post unzip steps ---------------------------------------------
import strutils, os, osproc
when defined(windows):
import registry
proc askBool(m: string): bool =
stdout.write m
while true:
let answer = stdin.readLine().normalize
case answer
of "y", "yes":
return true
of "n", "no":
return false
else:
echo "Please type 'y' or 'n'"
proc askNumber(m: string; a, b: int): int =
stdout.write m
stdout.write " [" & $a & ".." & $b & "] "
while true:
let answer = stdin.readLine()
try:
result = parseInt answer
if result < a or result > b:
raise newException(ValueError, "number out of range")
break
except ValueError:
echo "Please type in a number between ", a, " and ", b
proc patchConfig(mingw: string) =
const
cfgFile = "config/nim.cfg"
lookFor = """#gcc.path = r"$nim\dist\mingw\bin""""
replacePattern = """gcc.path = r"$1""""
try:
let cfg = readFile(cfgFile)
let newCfg = cfg.replace(lookFor, replacePattern % mingw)
if newCfg == cfg:
echo "Could not patch 'config/nim.cfg' [Error]"
echo "Reason: patch substring not found:"
echo lookFor
else:
writeFile(cfgFile, newCfg)
except IOError:
echo "Could not access 'config/nim.cfg' [Error]"
proc addToPathEnv(e: string) =
let p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
let x = if e.contains(Whitespace): "\"" & e & "\"" else: e
setUnicodeValue(r"Environment", "Path", p & ";" & x, HKEY_CURRENT_USER)
proc createShortcut(src, dest: string; icon = "") =
var cmd = "bin\\makelink.exe \"" & src & "\" \"\" \"" & dest &
".lnk\" \"\" 1 \"" & splitFile(src).dir & "\""
if icon.len != 0:
cmd.add " \"" & icon & "\" 0"
discard execShellCmd(cmd)
proc createStartMenuEntry() =
let appdata = getEnv("APPDATA")
if appdata.len == 0: return
let dest = appdata & r"\Microsoft\Windows\Start Menu\Programs\Nim-" &
NimVersion
if dirExists(dest): return
if askBool("Would like to add Nim-" & NimVersion &
" to your start menu? (y/n) "):
createDir(dest)
createShortcut(getCurrentDir() / "start.bat", dest / "Nim",
getCurrentDir() / r"icons\nim.ico")
if fileExists("doc/overview.html"):
createShortcut(getCurrentDir() / "doc" / "overview.html",
dest / "Overview")
if dirExists(r"dist\aporia-0.4.0"):
createShortcut(getCurrentDir() / r"dist\aporia-0.4.0\bin\aporia.exe",
dest / "Aporia")
proc checkGccArch(mingw: string): bool =
let gccExe = mingw / r"gcc.exe"
if fileExists(gccExe):
try:
let arch = execProcess(gccExe, ["-dumpmachine"], nil, {poStdErrToStdOut,
poUsePath})
when hostCPU == "i386":
result = arch.startsWith("i686-")
elif hostCPU == "amd64":
result = arch.startsWith("x86_64-")
else:
{.error: "Unknown CPU for Windows.".}
except OSError, IOError:
result = false
proc tryDirs(dirs: varargs[string]): string =
let bits = $(sizeof(pointer)*8)
for d in dirs:
if dirExists d:
let x = expandFilename(d / "bin")
if checkGccArch(x): return x
elif dirExists(d & bits):
let x = expandFilename((d & bits) / "bin")
if checkGccArch(x): return x
proc main() =
when defined(windows):
let desiredPath = expandFilename(getCurrentDir() / "bin")
let p = getUnicodeValue(r"Environment", "Path",
HKEY_CURRENT_USER)
var alreadyInPath = false
var mingWchoices: seq[string] = @[]
for x in p.split(';'):
let y = expandFilename(if x[0] == '"' and x[^1] == '"':
substr(x, 1, x.len-2) else: x)
if y == desiredPath: alreadyInPath = true
if y.toLowerAscii.contains("mingw"):
if dirExists(y) and checkGccArch(y):
mingWchoices.add y
if alreadyInPath:
echo "bin/nim.exe is already in your PATH [Skipping]"
else:
if askBool("nim.exe is not in your PATH environment variable.\n" &
" Should it be added permanently? (y/n) "):
addToPathEnv(desiredPath)
if mingWchoices.len == 0:
# No mingw in path, so try a few locations:
let alternative = tryDirs("dist/mingw", "../mingw", r"C:\mingw")
if alternative.len == 0:
echo "No MingW found in PATH and no candidate found " &
" in the standard locations [Error]"
else:
if askBool("Found a MingW directory that is not in your PATH.\n" &
alternative &
"\nShould it be added to your PATH permanently? (y/n) "):
addToPathEnv(alternative)
elif askBool("Do you want to patch Nim's config to use this? (y/n) "):
patchConfig(alternative)
elif mingWchoices.len == 1:
if askBool("MingW installation found at " & mingWchoices[0] & "\n" &
"Do you want to patch Nim's config to use this?\n" &
"(Not required since it's in your PATH!) (y/n) "):
patchConfig(mingWchoices[0])
else:
echo "Multiple MingW installations found: "
for i in 0..high(mingWchoices):
echo "[", i, "] ", mingWchoices[i]
if askBool("Do you want to patch Nim's config to use one of these? (y/n) "):
let idx = askNumber("Which one do you want to use for Nim? ",
1, len(mingWchoices))
patchConfig(mingWchoices[idx-1])
createStartMenuEntry()
else:
echo("Add ", getCurrentDir(), "/bin to your PATH...")
when isMainModule:
main()

View file

@ -30,6 +30,15 @@ There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing
the files over the UNIX hierarchy. However, updating your Nim installation the files over the UNIX hierarchy. However, updating your Nim installation
is more cumbersome then. is more cumbersome then.
To complete the installation you should also build Nim's tools like
``nimsuggest``, ``nimble`` or ``nimgrep`` via::
nim e install_tools.nims
Note that these tools should also end up in your ``PATH`` so adding
``$your_install_dir/bin/nim`` to your ``PATH`` is preferred over the symlink
solution.
Installation on the Macintosh Installation on the Macintosh
----------------------------- -----------------------------

View file

@ -16,4 +16,8 @@ mkDir "bin/nimblepkg"
for file in listFiles("nimble" & $id & "/src/nimblepkg/"): for file in listFiles("nimble" & $id & "/src/nimblepkg/"):
cpFile file, "bin/nimblepkg/" & file.extractFilename cpFile file, "bin/nimblepkg/" & file.extractFilename
mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe try:
mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
except OSError:
cpFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe

18
install_tools.nims Normal file
View file

@ -0,0 +1,18 @@
import ospaths
mode = ScriptMode.Verbose
if not dirExists"dist/nimble":
echo "[Error] This script only works for the tarball."
else:
let nimbleExe = "./bin/nimble".toExe
selfExec "c --noNimblePath -p:compiler -o:" & nimbleExe &
" dist/nimble/src/nimble.nim"
let nimsugExe = "./bin/nimsuggest".toExe
selfExec "c --noNimblePath -p:compiler -o:" & nimsugExe &
" dist/nimsuggest/nimsuggest.nim"
let nimgrepExe = "./bin/nimgrep".toExe
selfExec "c -o:./bin/nimgrep tools/nimgrep.nim"

153
koch.nim
View file

@ -20,11 +20,6 @@ import
const VersionAsString = system.NimVersion #"0.10.2" const VersionAsString = system.NimVersion #"0.10.2"
when defined(withUpdate):
import httpclient
when defined(haveZipLib):
import zipfiles
const const
HelpText = """ HelpText = """
+-----------------------------------------------------------------+ +-----------------------------------------------------------------+
@ -40,7 +35,7 @@ Options:
--help, -h shows this help and quits --help, -h shows this help and quits
Possible Commands: Possible Commands:
boot [options] bootstraps with given command line options boot [options] bootstraps with given command line options
install [bindir] installs to given directory; Unix only! distrohelper [bindir] helper for distro packagers
geninstall generate ./install.sh; Unix only! geninstall generate ./install.sh; Unix only!
testinstall test tar.xz package; Unix only! Only for devs! testinstall test tar.xz package; Unix only! Only for devs!
clean cleans Nim project; removes generated files clean cleans Nim project; removes generated files
@ -52,8 +47,6 @@ Possible Commands:
xz builds the installation XZ package xz builds the installation XZ package
nsis [options] builds the NSIS Setup installer (for Windows) nsis [options] builds the NSIS Setup installer (for Windows)
tests [options] run the testsuite tests [options] run the testsuite
update updates nim to the latest version from github
(compile koch with -d:withUpdate to enable)
temp options creates a temporary compiler for testing temp options creates a temporary compiler for testing
winrelease creates a release (for coredevs only) winrelease creates a release (for coredevs only)
Boot options: Boot options:
@ -69,7 +62,10 @@ Web options:
build the official docs, use UA-48159761-1 build the official docs, use UA-48159761-1
""" """
proc exe(f: string): string = return addFileExt(f, ExeExt) proc exe(f: string): string =
result = addFileExt(f, ExeExt)
when defined(windows):
result = result.replace('/','\\')
proc findNim(): string = proc findNim(): string =
var nim = "nim".exe var nim = "nim".exe
@ -123,6 +119,8 @@ proc testUnixInstall() =
execCleanPath("./koch boot -d:release", destDir / "bin") execCleanPath("./koch boot -d:release", destDir / "bin")
# check the docs build: # check the docs build:
execCleanPath("./koch web", destDir / "bin") execCleanPath("./koch web", destDir / "bin")
# check nimble builds:
execCleanPath("./bin/nim e install_tools.nims")
# check the tests work: # check the tests work:
execCleanPath("./koch tests", destDir / "bin") execCleanPath("./koch tests", destDir / "bin")
else: else:
@ -143,13 +141,13 @@ proc copyExe(source, dest: string) =
inclFilePermissions(dest, {fpUserExec}) inclFilePermissions(dest, {fpUserExec})
const const
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" compileNimInst = "tools/niminst/niminst"
proc csource(args: string) = proc csource(args: string) =
exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" % exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" %
[args, VersionAsString, compileNimInst, findNim()]) [args, VersionAsString, compileNimInst, findNim()])
proc bundleNimble() = proc bundleNimbleSrc() =
if dirExists("dist/nimble/.git"): if dirExists("dist/nimble/.git"):
exec("git --git-dir dist/nimble/.git pull") exec("git --git-dir dist/nimble/.git pull")
else: else:
@ -157,20 +155,38 @@ proc bundleNimble() =
let tags = execProcess("git --git-dir dist/nimble/.git tag -l v*").splitLines let tags = execProcess("git --git-dir dist/nimble/.git tag -l v*").splitLines
let tag = tags[^1] let tag = tags[^1]
exec("git --git-dir dist/nimble/.git checkout " & tag) exec("git --git-dir dist/nimble/.git checkout " & tag)
proc bundleNimbleExe() =
bundleNimbleSrc()
# now compile Nimble and copy it to $nim/bin for the installer.ini # now compile Nimble and copy it to $nim/bin for the installer.ini
# to pick it up: # to pick it up:
exec(findNim() & " c dist/nimble/src/nimble.nim") exec(findNim() & " c dist/nimble/src/nimble.nim")
copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe) copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
proc bundleNimsuggest(buildExe: bool) =
if dirExists("dist/nimsuggest/.git"):
exec("git --git-dir dist/nimsuggest/.git pull")
else:
exec("git clone https://github.com/nim-lang/nimsuggest.git dist/nimsuggest")
if buildExe:
exec(findNim() & " c --noNimblePath -p:compiler dist/nimsuggest/nimsuggest.nim")
copyExe("dist/nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
proc bundleFinishExe() =
exec(findNim() & " c finish.nim")
proc zip(args: string) = proc zip(args: string) =
bundleNimble() bundleNimbleSrc()
bundleNimsuggest(false)
bundleFinishExe()
exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
[VersionAsString, compileNimInst, findNim()]) [VersionAsString, compileNimInst, findNim()])
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" % exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
["tools/niminst/niminst".exe, VersionAsString]) ["tools/niminst/niminst".exe, VersionAsString])
proc xz(args: string) = proc xz(args: string) =
bundleNimble() bundleNimbleSrc()
bundleNimsuggest(false)
exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
[VersionAsString, compileNimInst, findNim()]) [VersionAsString, compileNimInst, findNim()])
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" % exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" %
@ -181,7 +197,9 @@ proc buildTool(toolname, args: string) =
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
proc nsis(args: string) = proc nsis(args: string) =
bundleNimble() bundleNimbleExe()
bundleNimsuggest(true)
bundleFinishExe()
# make sure we have generated the niminst executables: # make sure we have generated the niminst executables:
buildTool("tools/niminst/niminst", args) buildTool("tools/niminst/niminst", args)
#buildTool("tools/nimgrep", args) #buildTool("tools/nimgrep", args)
@ -195,11 +213,8 @@ proc geninstall(args="") =
exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" % exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" %
[findNim(), compileNimInst, VersionAsString, args]) [findNim(), compileNimInst, VersionAsString, args])
proc install(args: string) =
geninstall()
exec("sh ./install.sh $#" % args)
proc web(args: string) = proc web(args: string) =
exec("$# js tools/dochack/dochack.nim" % findNim())
exec("$# cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" % exec("$# cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" %
[findNim(), args, VersionAsString]) [findNim(), args, VersionAsString])
@ -244,11 +259,13 @@ proc boot(args: string) =
var finalDest = "bin" / "nim".exe var finalDest = "bin" / "nim".exe
# default to use the 'c' command: # default to use the 'c' command:
let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: "" let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: ""
let smartNimcache = if "release" in args: "rnimcache" else: "dnimcache"
copyExe(findStartNim(), 0.thVersion) copyExe(findStartNim(), 0.thVersion)
for i in 0..2: for i in 0..2:
echo "iteration: ", i+1 echo "iteration: ", i+1
exec i.thVersion & " $# $# compiler" / "nim.nim" % [bootOptions, args] exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
smartNimcache]
if sameFileContent(output, i.thVersion): if sameFileContent(output, i.thVersion):
copyExe(output, finalDest) copyExe(output, finalDest)
echo "executables are equal: SUCCESS!" echo "executables are equal: SUCCESS!"
@ -273,7 +290,7 @@ proc cleanAux(dir: string) =
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
case kind case kind
of pcFile: of pcFile:
var (dir, name, ext) = splitFile(path) var (_, name, ext) = splitFile(path)
if ext == "" or cleanExt.contains(ext): if ext == "" or cleanExt.contains(ext):
if not ignore.contains(name): if not ignore.contains(name):
echo "removing: ", path echo "removing: ", path
@ -302,99 +319,14 @@ proc clean(args: string) =
echo "removing dir: ", path echo "removing dir: ", path
removeDir(path) removeDir(path)
# -------------- update -------------------------------------------------------
when defined(withUpdate):
when defined(windows):
{.warning: "Windows users: Make sure to run 'koch update' in Bash.".}
proc update(args: string) =
when defined(windows):
echo("Windows users: Make sure to be running this in Bash. ",
"If you aren't, press CTRL+C now.")
var thisDir = getAppDir()
var git = findExe("git")
echo("Checking for git repo and git executable...")
if existsDir(thisDir & "/.git") and git != "":
echo("Git repo found!")
# use git to download latest source
echo("Checking for updates...")
discard startCmd(git & " fetch origin master")
var procs = startCmd(git & " diff origin/master master")
var errcode = procs.waitForExit()
var output = readLine(procs.outputStream)
echo(output)
if errcode == 0:
if output == "":
# No changes
echo("No update. Exiting...")
return
else:
echo("Fetching updates from repo...")
var pullout = execCmdEx(git & " pull origin master")
if pullout[1] != 0:
quit("An error has occurred.")
else:
if pullout[0].startsWith("Already up-to-date."):
quit("No new changes fetched from the repo. " &
"Local branch must be ahead of it. Exiting...")
else:
quit("An error has occurred.")
else:
echo("No repo or executable found!")
when defined(haveZipLib):
echo("Falling back.. Downloading source code from repo...")
# use dom96's httpclient to download zip
downloadFile("https://github.com/Araq/Nim/zipball/master",
thisDir / "update.zip")
try:
echo("Extracting source code from archive...")
var zip: TZipArchive
discard open(zip, thisDir & "/update.zip", fmRead)
extractAll(zip, thisDir & "/")
except:
quit("Error reading archive.")
else:
quit("No failback available. Exiting...")
echo("Starting update...")
boot(args)
echo("Update complete!")
# -------------- builds a release --------------------------------------------- # -------------- builds a release ---------------------------------------------
proc run7z(platform: string, patterns: varargs[string]) =
const tmpDir = "nim-" & VersionAsString
createDir tmpDir
try:
for pattern in patterns:
for f in walkFiles(pattern):
if "nimcache" notin f:
copyFile(f, tmpDir / f)
exec("7z a -tzip $1-$2.zip $1" % [tmpDir, platform])
finally:
removeDir tmpDir
proc winRelease() = proc winRelease() =
boot(" -d:release") exec(r"call ci\nsis_build.bat " & VersionAsString)
#buildTool("tools/niminst/niminst", " -d:release")
buildTool("tools/nimgrep", " -d:release")
buildTool("compiler/nimfix/nimfix", " -d:release")
buildTool("compiler/nimsuggest/nimsuggest", " -d:release")
#run7z("win32", "bin/nim.exe", "bin/c2nim.exe", "bin/nimgrep.exe",
# "bin/nimfix.exe",
# "bin/nimble.exe", "bin/*.dll",
# "config", "dist/*.dll", "examples", "lib",
# "readme.txt", "contributors.txt", "copying.txt")
# second step: XXX build 64 bit version
# -------------- tests -------------------------------------------------------- # -------------- tests --------------------------------------------------------
template `|`(a, b): expr = (if a.len > 0: a else: b) template `|`(a, b): string = (if a.len > 0: a else: b)
proc tests(args: string) = proc tests(args: string) =
# we compile the tester with taintMode:on to have a basic # we compile the tester with taintMode:on to have a basic
@ -444,14 +376,9 @@ of cmdArgument:
of "xz": xz(op.cmdLineRest) of "xz": xz(op.cmdLineRest)
of "nsis": nsis(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest)
of "geninstall": geninstall(op.cmdLineRest) of "geninstall": geninstall(op.cmdLineRest)
of "install": install(op.cmdLineRest) of "distrohelper": geninstall()
of "testinstall": testUnixInstall() of "testinstall": testUnixInstall()
of "test", "tests": tests(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest)
of "update":
when defined(withUpdate):
update(op.cmdLineRest)
else:
quit "this Koch has not been compiled with -d:withUpdate"
of "temp": temp(op.cmdLineRest) of "temp": temp(op.cmdLineRest)
of "winrelease": winRelease() of "winrelease": winRelease()
else: showHelp() else: showHelp()

View file

@ -6,6 +6,9 @@
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# Architecture-specific optimizations and features.
# arch.nim can be imported by only a subset of the
# architectures supported by Nim.
when defined(windows): when defined(windows):
const const

View file

@ -333,7 +333,7 @@ proc parseStmt*(s: string): NimNode {.noSideEffect, compileTime.} =
let x = internalErrorFlag() let x = internalErrorFlag()
if x.len > 0: raise newException(ValueError, x) if x.len > 0: raise newException(ValueError, x)
proc getAst*(macroOrTemplate: expr): NimNode {.magic: "ExpandToAst", noSideEffect.} proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEffect.}
## Obtains the AST nodes returned from a macro or template invocation. ## Obtains the AST nodes returned from a macro or template invocation.
## Example: ## Example:
## ##
@ -342,7 +342,7 @@ proc getAst*(macroOrTemplate: expr): NimNode {.magic: "ExpandToAst", noSideEffec
## macro FooMacro() = ## macro FooMacro() =
## var ast = getAst(BarTemplate()) ## var ast = getAst(BarTemplate())
proc quote*(bl: stmt, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.}
## Quasi-quoting operator. ## Quasi-quoting operator.
## Accepts an expression or a block and returns the AST that represents it. ## Accepts an expression or a block and returns the AST that represents it.
## Within the quoted AST, you are able to interpolate NimNode expressions ## Within the quoted AST, you are able to interpolate NimNode expressions
@ -663,7 +663,7 @@ proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}=
for i in 0 .. < src.len: for i in 0 .. < src.len:
dest.add src[i].copyNimTree dest.add src[i].copyNimTree
template expectRoutine(node: NimNode): stmt = template expectRoutine(node: NimNode) =
expectKind(node, RoutineNodes) expectKind(node, RoutineNodes)
proc name*(someProc: NimNode): NimNode {.compileTime.} = proc name*(someProc: NimNode): NimNode {.compileTime.} =
@ -870,7 +870,7 @@ proc hasArgOfName* (params: NimNode; name: string): bool {.compiletime.}=
## Search nnkFormalParams for an argument. ## Search nnkFormalParams for an argument.
assert params.kind == nnkFormalParams assert params.kind == nnkFormalParams
for i in 1 .. <params.len: for i in 1 .. <params.len:
template node: expr = params[i] template node: untyped = params[i]
if name.eqIdent( $ node[0]): if name.eqIdent( $ node[0]):
return true return true
@ -891,7 +891,7 @@ proc boolVal*(n: NimNode): bool {.compileTime, noSideEffect.} =
else: n == bindSym"true" # hacky solution for now else: n == bindSym"true" # hacky solution for now
when not defined(booting): when not defined(booting):
template emit*(e: static[string]): stmt {.deprecated.} = template emit*(e: static[string]): untyped {.deprecated.} =
## accepts a single string argument and treats it as nim code ## accepts a single string argument and treats it as nim code
## that should be inserted verbatim in the program ## that should be inserted verbatim in the program
## Example: ## Example:
@ -900,6 +900,6 @@ when not defined(booting):
## emit("echo " & '"' & "hello world".toUpper & '"') ## emit("echo " & '"' & "hello world".toUpper & '"')
## ##
## Deprecated since version 0.15 since it's so rarely useful. ## Deprecated since version 0.15 since it's so rarely useful.
macro payload: stmt {.gensym.} = macro payload: untyped {.gensym.} =
result = parseStmt(e) result = parseStmt(e)
payload() payload()

View file

@ -359,7 +359,7 @@ proc parallelReplace*(s: string, subs: openArray[
proc transformFile*(infile, outfile: string, proc transformFile*(infile, outfile: string,
subs: openArray[tuple[pattern: Regex, repl: string]]) = subs: openArray[tuple[pattern: Regex, repl: string]]) =
## reads in the file `infile`, performs a parallel replacement (calls ## reads in the file `infile`, performs a parallel replacement (calls
## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an ## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
## error occurs. This is supposed to be used for quick scripting. ## error occurs. This is supposed to be used for quick scripting.
var x = readFile(infile).string var x = readFile(infile).string
writeFile(outfile, x.parallelReplace(subs)) writeFile(outfile, x.parallelReplace(subs))

View file

@ -415,16 +415,6 @@ struct TFrame {
#define NIM_POSIX_INIT __attribute__((constructor)) #define NIM_POSIX_INIT __attribute__((constructor))
#if defined(_MSCVER) && defined(__i386__)
__declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) {
__asm {
lock xadd dword ptr [ECX], EDX
mov EAX, EDX
ret
}
}
#endif
#ifdef __GNUC__ #ifdef __GNUC__
# define likely(x) __builtin_expect(x, 1) # define likely(x) __builtin_expect(x, 1)
# define unlikely(x) __builtin_expect(x, 0) # define unlikely(x) __builtin_expect(x, 0)

View file

@ -415,18 +415,28 @@ proc sortIndex(a: var openArray[IndexEntry]) =
a[j] <- v a[j] <- v
if h == 1: break if h == 1: break
proc escapeLink(s: string): string =
result = newStringOfCap(s.len + s.len shr 2)
for c in items(s):
case c
of 'a'..'z', '_', 'A'..'Z', '0'..'9', '.', '#', ',', '/':
result.add c
else:
add(result, "%")
add(result, toHex(ord(c), 2))
proc generateSymbolIndex(symbols: seq[IndexEntry]): string = proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
result = "" result = "<dl>"
var i = 0 var i = 0
while i < symbols.len: while i < symbols.len:
let keyword= symbols[i].keyword let keyword = symbols[i].keyword
let cleaned_keyword = keyword[1..keyword.high - 1] let cleaned_keyword = keyword.escapeLink
result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><ul class=\"simple\"><dd>\n", result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><dd><ul class=\"simple\">\n",
[keyword, cleaned_keyword]) [keyword, cleaned_keyword])
var j = i var j = i
while j < symbols.len and keyword == symbols[j].keyword: while j < symbols.len and keyword == symbols[j].keyword:
let let
url = symbols[j].link url = symbols[j].link.escapeLink
text = if not symbols[j].linkTitle.isNil: symbols[j].linkTitle else: url text = if not symbols[j].linkTitle.isNil: symbols[j].linkTitle else: url
desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: "" desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
if desc.len > 0: if desc.len > 0:
@ -439,6 +449,7 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
inc j inc j
result.add("</ul></dd>\n") result.add("</ul></dd>\n")
i = j i = j
result.add("</dl>")
proc isDocumentationTitle(hyperlink: string): bool = proc isDocumentationTitle(hyperlink: string): bool =
## Returns true if the hyperlink is actually a documentation title. ## Returns true if the hyperlink is actually a documentation title.
@ -463,9 +474,9 @@ proc indentToLevel(level: var int, newLevel: int): string =
if level == newLevel: if level == newLevel:
return return
if newLevel > level: if newLevel > level:
result = repeat("<ul>", newLevel - level) result = repeat("<li><ul>", newLevel - level)
else: else:
result = repeat("</ul>", level - newLevel) result = repeat("</ul></li>", level - newLevel)
level = newLevel level = newLevel
proc generateDocumentationTOC(entries: seq[IndexEntry]): string = proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
@ -503,7 +514,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
else: else:
result.add(level.indentToLevel(levels[L].level)) result.add(level.indentToLevel(levels[L].level))
result.add("<li><a href=\"" & link & "\">" & result.add("<li><a href=\"" & link & "\">" &
levels[L].text & "</a>\n") levels[L].text & "</a></li>\n")
inc L inc L
result.add(level.indentToLevel(1) & "</ul>\n") result.add(level.indentToLevel(1) & "</ul>\n")
assert(not titleRef.isNil, assert(not titleRef.isNil,
@ -520,7 +531,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
for title in titles: for title in titles:
let tocList = generateDocumentationTOC(docs.getOrDefault(title)) let tocList = generateDocumentationTOC(docs.getOrDefault(title))
result.add("<ul><li><a href=\"" & result.add("<ul><li><a href=\"" &
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n") title.link & "\">" & title.keyword & "</a>\n" & tocList & "</li></ul>\n")
proc generateDocumentationJumps(docs: IndexedDocs): string = proc generateDocumentationJumps(docs: IndexedDocs): string =
## Returns a plain list of hyperlinks to documentation TOCs in HTML. ## Returns a plain list of hyperlinks to documentation TOCs in HTML.
@ -783,7 +794,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)]) if s.valid: dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options]) if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
if arg.valid: if arg.valid:
let htmlOut = if isObject: let htmlOut = if isObject:
"<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>" "<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"

View file

@ -1694,6 +1694,8 @@ var
INADDR_ANY* {.importc, header: "<netinet/in.h>".}: InAddrScalar INADDR_ANY* {.importc, header: "<netinet/in.h>".}: InAddrScalar
## IPv4 local host address. ## IPv4 local host address.
INADDR_LOOPBACK* {.importc, header: "<netinet/in.h>".}: InAddrScalar
## IPv4 loopback address.
INADDR_BROADCAST* {.importc, header: "<netinet/in.h>".}: InAddrScalar INADDR_BROADCAST* {.importc, header: "<netinet/in.h>".}: InAddrScalar
## IPv4 broadcast address. ## IPv4 broadcast address.
@ -2653,6 +2655,12 @@ proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {.
proc realpath*(name, resolved: cstring): cstring {. proc realpath*(name, resolved: cstring): cstring {.
importc: "realpath", header: "<stdlib.h>".} importc: "realpath", header: "<stdlib.h>".}
proc mkstemp*(tmpl: cstring): cint {.importc, header: "<stdlib.h>".}
## Create a temporary file.
##
## **Warning**: The `tmpl` argument is written to by `mkstemp` and thus
## can't be a string literal. If in doubt copy the string before passing it.
proc utimes*(path: cstring, times: ptr array[2, Timeval]): int {. proc utimes*(path: cstring, times: ptr array[2, Timeval]): int {.
importc: "utimes", header: "<sys/time.h>".} importc: "utimes", header: "<sys/time.h>".}
## Sets file access and modification times. ## Sets file access and modification times.

View file

@ -259,3 +259,13 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
# Get process group ID for session leader for controlling terminal FD. # Get process group ID for session leader for controlling terminal FD.
proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".} proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}
# Window size ioctl. Should work on on any Unix that xterm has been ported to.
var TIOCGWINSZ*{.importc, header: "<sys/ioctl.h>".}: culong
type IOctl_WinSize* {.importc: "struct winsize", header: "<sys/ioctl.h>",
final, pure.} = object
ws_row*, ws_col*, ws_xpixel*, ws_ypixel*: cushort
proc ioctl*(fd: cint, request: culong, reply: ptr IOctl_WinSize): int {.
importc: "ioctl", header: "<stdio.h>", varargs.}

View file

@ -179,7 +179,7 @@ proc sort*[T](a: var openArray[T],
## sort(myStrArray, system.cmp) ## sort(myStrArray, system.cmp)
## ##
## You can inline adhoc comparison procs with the `do notation ## You can inline adhoc comparison procs with the `do notation
## <manual.html#do-notation>`_. Example: ## <manual.html#procedures-do-notation>`_. Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##

View file

@ -9,7 +9,7 @@
include "system/inclrtl" include "system/inclrtl"
import os, oids, tables, strutils, macros, times, heapqueue import os, oids, tables, strutils, times, heapqueue
import nativesockets, net, queues import nativesockets, net, queues
@ -105,6 +105,36 @@ export Port, SocketFlag
## be used to await a procedure returning a ``Future[void]``: ## be used to await a procedure returning a ``Future[void]``:
## ``await socket.send("foobar")``. ## ``await socket.send("foobar")``.
## ##
## If an awaited future completes with an error, then ``await`` will re-raise
## this error. To avoid this, you can use the ``yield`` keyword instead of
## ``await``. The following section shows different ways that you can handle
## exceptions in async procs.
##
## Handling Exceptions
## ~~~~~~~~~~~~~~~~~~~
##
## The most reliable way to handle exceptions is to use ``yield`` on a future
## then check the future's ``failed`` property. For example:
##
## .. code-block:: Nim
## var future = sock.recv(100)
## yield future
## if future.failed:
## # Handle exception
##
## The ``async`` procedures also offer limited support for the try statement.
##
## .. code-block:: Nim
## try:
## let data = await sock.recv(100)
## echo("Received ", data)
## except:
## # Handle exception
##
## Unfortunately the semantics of the try statement may not always be correct,
## and occassionally the compilation may fail altogether.
## As such it is better to use the former style when possible.
##
## Discarding futures ## Discarding futures
## ------------------ ## ------------------
## ##
@ -126,279 +156,10 @@ export Port, SocketFlag
## * Can't await in a ``except`` body ## * Can't await in a ``except`` body
## * Forward declarations for async procs are broken, ## * Forward declarations for async procs are broken,
## link includes workaround: https://github.com/nim-lang/Nim/issues/3182. ## link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
## * FutureVar[T] needs to be completed manually.
# TODO: Check if yielded future is nil and throw a more meaningful exception # TODO: Check if yielded future is nil and throw a more meaningful exception
# -- Futures include includes/asyncfutures
type
FutureBase* = ref object of RootObj ## Untyped future.
cb: proc () {.closure,gcsafe.}
finished: bool
error*: ref Exception ## Stored exception
errorStackTrace*: string
when not defined(release):
stackTrace: string ## For debugging purposes only.
id: int
fromProc: string
Future*[T] = ref object of FutureBase ## Typed future.
value: T ## Stored value
FutureVar*[T] = distinct Future[T]
FutureError* = object of Exception
cause*: FutureBase
{.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
when not defined(release):
var currentID = 0
proc callSoon*(cbproc: proc ()) {.gcsafe.}
proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
## Creates a new future.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
new(result)
result.finished = false
when not defined(release):
result.stackTrace = getStackTrace()
result.id = currentID
result.fromProc = fromProc
currentID.inc()
proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
## Create a new ``FutureVar``. This Future type is ideally suited for
## situations where you want to avoid unnecessary allocations of Futures.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
result = FutureVar[T](newFuture[T](fromProc))
proc clean*[T](future: FutureVar[T]) =
## Resets the ``finished`` status of ``future``.
Future[T](future).finished = false
Future[T](future).error = nil
proc checkFinished[T](future: Future[T]) =
## Checks whether `future` is finished. If it is then raises a
## ``FutureError``.
when not defined(release):
if future.finished:
var msg = ""
msg.add("An attempt was made to complete a Future more than once. ")
msg.add("Details:")
msg.add("\n Future ID: " & $future.id)
msg.add("\n Created in proc: " & future.fromProc)
msg.add("\n Stack trace to moment of creation:")
msg.add("\n" & indent(future.stackTrace.strip(), 4))
when T is string:
msg.add("\n Contents (string): ")
msg.add("\n" & indent(future.value.repr, 4))
msg.add("\n Stack trace to moment of secondary completion:")
msg.add("\n" & indent(getStackTrace().strip(), 4))
var err = newException(FutureError, msg)
err.cause = future
raise err
proc complete*[T](future: Future[T], val: T) =
## Completes ``future`` with value ``val``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future)
assert(future.error == nil)
future.value = val
future.finished = true
if future.cb != nil:
future.cb()
proc complete*(future: Future[void]) =
## Completes a void ``future``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future)
assert(future.error == nil)
future.finished = true
if future.cb != nil:
future.cb()
proc complete*[T](future: FutureVar[T]) =
## Completes a ``FutureVar``.
template fut: expr = Future[T](future)
checkFinished(fut)
assert(fut.error == nil)
fut.finished = true
if fut.cb != nil:
fut.cb()
proc fail*[T](future: Future[T], error: ref Exception) =
## Completes ``future`` with ``error``.
#assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future)
future.finished = true
future.error = error
future.errorStackTrace =
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
if future.cb != nil:
future.cb()
else:
# This is to prevent exceptions from being silently ignored when a future
# is discarded.
# TODO: This may turn out to be a bad idea.
# Turns out this is a bad idea.
#raise error
discard
proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: You most likely want the other ``callback`` setter which
## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
callSoon(future.cb)
proc `callback=`*[T](future: Future[T],
cb: proc (future: Future[T]) {.closure,gcsafe.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.callback = proc () = cb(future)
proc injectStacktrace[T](future: Future[T]) =
# TODO: Come up with something better.
when not defined(release):
var msg = ""
msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:")
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
else:
msg.add("\n Empty or nil stack trace.")
future.error.msg.add(msg)
proc read*[T](future: Future[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``ValueError`` exception.
##
## If the result of the future is an error then that error will be raised.
if future.finished:
if future.error != nil:
injectStacktrace(future)
raise future.error
when T isnot void:
return future.value
else:
# TODO: Make a custom exception type for this?
raise newException(ValueError, "Future still in progress.")
proc readError*[T](future: Future[T]): ref Exception =
## Retrieves the exception stored in ``future``.
##
## An ``ValueError`` exception will be thrown if no exception exists
## in the specified Future.
if future.error != nil: return future.error
else:
raise newException(ValueError, "No error in future.")
proc mget*[T](future: FutureVar[T]): var T =
## Returns a mutable value stored in ``future``.
##
## Unlike ``read``, this function will not raise an exception if the
## Future has not been finished.
result = Future[T](future).value
proc finished*[T](future: Future[T]): bool =
## Determines whether ``future`` has completed.
##
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
future.finished
proc failed*(future: FutureBase): bool =
## Determines whether ``future`` completed with an error.
return future.error != nil
proc asyncCheck*[T](future: Future[T]) =
## Sets a callback on ``future`` which raises an exception if the future
## finished with an error.
##
## This should be used instead of ``discard`` to discard void futures.
future.callback =
proc () =
if future.failed:
injectStacktrace(future)
raise future.error
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once both ``fut1`` and ``fut2``
## complete.
var retFuture = newFuture[void]("asyncdispatch.`and`")
fut1.callback =
proc () =
if fut2.finished: retFuture.complete()
fut2.callback =
proc () =
if fut1.finished: retFuture.complete()
return retFuture
proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once either ``fut1`` or ``fut2``
## complete.
var retFuture = newFuture[void]("asyncdispatch.`or`")
proc cb() =
if not retFuture.finished: retFuture.complete()
fut1.callback = cb
fut2.callback = cb
return retFuture
proc all*[T](futs: varargs[Future[T]]): auto =
## Returns a future which will complete once
## all futures in ``futs`` complete.
##
## If the awaited futures are not ``Future[void]``, the returned future
## will hold the values of all awaited futures in a sequence.
##
## If the awaited futures *are* ``Future[void]``,
## this proc returns ``Future[void]``.
when T is void:
var
retFuture = newFuture[void]("asyncdispatch.all")
completedFutures = 0
let totalFutures = len(futs)
for fut in futs:
fut.callback = proc(f: Future[T]) =
inc(completedFutures)
if completedFutures == totalFutures:
retFuture.complete()
return retFuture
else:
var
retFuture = newFuture[seq[T]]("asyncdispatch.all")
retValues = newSeq[T](len(futs))
completedFutures = 0
for i, fut in futs:
proc setCallback(i: int) =
fut.callback = proc(f: Future[T]) =
retValues[i] = f.read()
inc(completedFutures)
if completedFutures == len(retValues):
retFuture.complete(retValues)
setCallback(i)
return retFuture
type type
PDispatcherBase = ref object of RootRef PDispatcherBase = ref object of RootRef
@ -500,48 +261,49 @@ when defined(windows) or defined(nimdoc):
raise newException(ValueError, raise newException(ValueError,
"No handles or timers registered in dispatcher.") "No handles or timers registered in dispatcher.")
let at = p.adjustedTimeout(timeout) if p.handles.len != 0:
var llTimeout = let at = p.adjustedTimeout(timeout)
if at == -1: winlean.INFINITE var llTimeout =
else: at.int32 if at == -1: winlean.INFINITE
else: at.int32
var lpNumberOfBytesTransferred: Dword var lpNumberOfBytesTransferred: Dword
var lpCompletionKey: ULONG_PTR var lpCompletionKey: ULONG_PTR
var customOverlapped: PCustomOverlapped var customOverlapped: PCustomOverlapped
let res = getQueuedCompletionStatus(p.ioPort, let res = getQueuedCompletionStatus(p.ioPort,
addr lpNumberOfBytesTransferred, addr lpCompletionKey, addr lpNumberOfBytesTransferred, addr lpCompletionKey,
cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool
# http://stackoverflow.com/a/12277264/492186 # http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
if res: if res:
# This is useful for ensuring the reliability of the overlapped struct. # This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, OSErrorCode(-1))
# If cell.data != nil, then system.protect(rawEnv(cb)) was called,
# so we need to dispose our `cb` environment, because it is not needed
# anymore.
if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped)
else:
let errCode = osLastError()
if customOverlapped != nil:
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd, customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, errCode) lpNumberOfBytesTransferred, OSErrorCode(-1))
# If cell.data != nil, then system.protect(rawEnv(cb)) was called,
# so we need to dispose our `cb` environment, because it is not needed
# anymore.
if customOverlapped.data.cell.data != nil: if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell) system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped) GC_unref(customOverlapped)
else: else:
if errCode.int32 == WAIT_TIMEOUT: let errCode = osLastError()
# Timed out if customOverlapped != nil:
discard assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
else: raiseOSError(errCode) customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, errCode)
if customOverlapped.data.cell.data != nil:
system.dispose(customOverlapped.data.cell)
GC_unref(customOverlapped)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
discard
else: raiseOSError(errCode)
# Timer processing. # Timer processing.
processTimers(p) processTimers(p)
@ -749,8 +511,8 @@ when defined(windows) or defined(nimdoc):
retFuture.complete("") retFuture.complete("")
return retFuture return retFuture
proc recvInto*(socket: AsyncFD, buf: cstring, size: int, proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] = flags = {SocketFlag.SafeDisconn}): Future[int] =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
## at least be of that size. Returned future will complete once all the ## at least be of that size. Returned future will complete once all the
## data requested is read, a part of the data has been read, or the socket ## data requested is read, a part of the data has been read, or the socket
@ -773,7 +535,7 @@ when defined(windows) or defined(nimdoc):
#buf[] = '\0' #buf[] = '\0'
var dataBuf: TWSABuf var dataBuf: TWSABuf
dataBuf.buf = buf dataBuf.buf = cast[cstring](buf)
dataBuf.len = size.ULONG dataBuf.len = size.ULONG
var bytesReceived: Dword var bytesReceived: Dword
@ -819,16 +581,18 @@ when defined(windows) or defined(nimdoc):
retFuture.complete(bytesReceived) retFuture.complete(bytesReceived)
return retFuture return retFuture
proc send*(socket: AsyncFD, data: string, proc send*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
## data has been sent. ## data has been sent.
## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, you must use GC_ref/GC_unref calls
## to avoid early freeing of the buffer
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
var dataBuf: TWSABuf var dataBuf: TWSABuf
dataBuf.buf = data # since this is not used in a callback, this is fine dataBuf.buf = cast[cstring](buf)
dataBuf.len = data.len.ULONG dataBuf.len = size.ULONG
var bytesReceived, lowFlags: Dword var bytesReceived, lowFlags: Dword
var ol = PCustomOverlapped() var ol = PCustomOverlapped()
@ -1281,43 +1045,45 @@ else:
proc poll*(timeout = 500) = proc poll*(timeout = 500) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
for info in p.selector.select(p.adjustedTimeout(timeout)):
let data = PData(info.key.data)
assert data.fd == info.key.fd.AsyncFD
#echo("In poll ", data.fd.cint)
# There may be EvError here, but we handle them in callbacks,
# so that exceptions can be raised from `send(...)` and
# `recv(...)` routines.
if EvRead in info.events: if p.selector.len > 0:
# Callback may add items to ``data.readCBs`` which causes issues if for info in p.selector.select(p.adjustedTimeout(timeout)):
# we are iterating over ``data.readCBs`` at the same time. We therefore let data = PData(info.key.data)
# make a copy to iterate over. assert data.fd == info.key.fd.AsyncFD
let currentCBs = data.readCBs #echo("In poll ", data.fd.cint)
data.readCBs = @[] # There may be EvError here, but we handle them in callbacks,
for cb in currentCBs: # so that exceptions can be raised from `send(...)` and
if not cb(data.fd): # `recv(...)` routines.
# Callback wants to be called again.
data.readCBs.add(cb)
if EvWrite in info.events: if EvRead in info.events:
let currentCBs = data.writeCBs # Callback may add items to ``data.readCBs`` which causes issues if
data.writeCBs = @[] # we are iterating over ``data.readCBs`` at the same time. We therefore
for cb in currentCBs: # make a copy to iterate over.
if not cb(data.fd): let currentCBs = data.readCBs
# Callback wants to be called again. data.readCBs = @[]
data.writeCBs.add(cb) for cb in currentCBs:
if not cb(data.fd):
# Callback wants to be called again.
data.readCBs.add(cb)
if info.key in p.selector: if EvWrite in info.events:
var newEvents: set[Event] let currentCBs = data.writeCBs
if data.readCBs.len != 0: newEvents = {EvRead} data.writeCBs = @[]
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} for cb in currentCBs:
if newEvents != info.key.events: if not cb(data.fd):
update(data.fd, newEvents) # Callback wants to be called again.
else: data.writeCBs.add(cb)
# FD no longer a part of the selector. Likely been closed
# (e.g. socket disconnected). if info.key in p.selector:
discard var newEvents: set[Event]
if data.readCBs.len != 0: newEvents = {EvRead}
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
if newEvents != info.key.events:
update(data.fd, newEvents)
else:
# FD no longer a part of the selector. Likely been closed
# (e.g. socket disconnected).
discard
# Timer processing. # Timer processing.
processTimers(p) processTimers(p)
@ -1398,7 +1164,7 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc recvInto*(socket: AsyncFD, buf: cstring, size: int, proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] = flags = {SocketFlag.SafeDisconn}): Future[int] =
var retFuture = newFuture[int]("recvInto") var retFuture = newFuture[int]("recvInto")
@ -1422,7 +1188,7 @@ else:
addRead(socket, cb) addRead(socket, cb)
return retFuture return retFuture
proc send*(socket: AsyncFD, data: string, proc send*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[void] = flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
@ -1430,8 +1196,8 @@ else:
proc cb(sock: AsyncFD): bool = proc cb(sock: AsyncFD): bool =
result = true result = true
let netSize = data.len-written let netSize = size-written
var d = data.cstring var d = cast[cstring](buf)
let res = send(sock.SocketHandle, addr d[written], netSize.cint, let res = send(sock.SocketHandle, addr d[written], netSize.cint,
MSG_NOSIGNAL) MSG_NOSIGNAL)
if res < 0: if res < 0:
@ -1576,368 +1342,31 @@ proc accept*(socket: AsyncFD,
retFut.complete(future.read.client) retFut.complete(future.read.client)
return retFut return retFut
proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent.
var retFuture = newFuture[void]("send")
var copiedData = data
GC_ref(copiedData) # we need to protect data until send operation is completed
# or failed.
let sendFut = socket.send(addr copiedData[0], data.len, flags)
sendFut.callback =
proc () =
GC_unref(copiedData)
if sendFut.failed:
retFuture.fail(sendFut.error)
else:
retFuture.complete()
return retFuture
# -- Await Macro # -- Await Macro
include asyncmacro
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} = proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
# Skips a nest of StmtList's.
result = node
if node[0].kind == nnkStmtList:
result = skipUntilStmtList(node[0])
proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
result = node
if node[0].kind == nnkStmtList:
result = node[0]
template createCb(retFutureSym, iteratorNameSym,
name: untyped) =
var nameIterVar = iteratorNameSym
#{.push stackTrace: off.}
proc cb {.closure,gcsafe.} =
try:
if not nameIterVar.finished:
var next = nameIterVar()
if next == nil:
assert retFutureSym.finished, "Async procedure's (" &
name & ") return Future was not finished."
else:
next.callback = cb
except:
if retFutureSym.finished:
# Take a look at tasyncexceptions for the bug which this fixes.
# That test explains it better than I can here.
raise
else:
retFutureSym.fail(getCurrentException())
cb()
#{.pop.}
proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
if tryStmt.kind == nnkNilLit:
result = rootReceiver
else:
var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
let errorNode = newDotExpr(futSym, newIdentNode("error"))
for i in 1 .. <tryStmt.len:
let exceptBranch = tryStmt[i]
if exceptBranch[0].kind == nnkStmtList:
exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
else:
var exceptIdentCount = 0
var ifCond: NimNode
for i in 0 .. <exceptBranch.len:
let child = exceptBranch[i]
if child.kind == nnkIdent:
let cond = infix(errorNode, "of", child)
if exceptIdentCount == 0:
ifCond = cond
else:
ifCond = infix(ifCond, "or", cond)
else:
break
exceptIdentCount.inc
expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
# -> -> else: raise futSym.error
exceptionChecks.add((newIdentNode("true"),
newNimNode(nnkRaiseStmt).add(errorNode)))
# Read the future if there is no error.
# -> else: futSym.read
let elseNode = newNimNode(nnkElse, fromNode)
elseNode.add newNimNode(nnkStmtList, fromNode)
elseNode[0].add rootReceiver
let ifBody = newStmtList()
ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
ifBody.add newIfStmt(exceptionChecks)
ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
result = newIfStmt(
(newDotExpr(futSym, newIdentNode("failed")), ifBody)
)
result.add elseNode
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
rootReceiver: expr, fromNode: NimNode) =
## Params:
## futureVarNode: The NimNode which is a symbol identifying the Future[T]
## variable to yield.
## fromNode: Used for better debug information (to give context).
## valueReceiver: The node which defines an expression that retrieves the
## future's value.
##
## rootReceiver: ??? TODO
# -> yield future<x>
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
fromNode)
template createVar(result: var NimNode, futSymName: string,
asyncProc: NimNode,
valueReceiver, rootReceiver: expr,
fromNode: NimNode) =
result = newNimNode(nnkStmtList, fromNode)
var futSym = genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool,
tryStmt: NimNode): NimNode {.compileTime.} =
#echo(node.treeRepr)
result = node
case node.kind
of nnkReturnStmt:
result = newNimNode(nnkStmtList, node)
if node[0].kind == nnkEmpty:
if not subTypeIsVoid:
result.add newCall(newIdentNode("complete"), retFutureSym,
newIdentNode("result"))
else:
result.add newCall(newIdentNode("complete"), retFutureSym)
else:
let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt)
if x.kind == nnkYieldStmt: result.add x
else:
result.add newCall(newIdentNode("complete"), retFutureSym, x)
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].ident == !"await":
case node[1].kind
of nnkIdent, nnkInfix, nnkDotExpr:
# await x
# await x or y
result = newNimNode(nnkYieldStmt, node).add(node[1]) # -> yield x
of nnkCall, nnkCommand:
# await foo(p, x)
# await foo p, x
var futureValue: NimNode
result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
futureValue, node)
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
# foo await x
var newCommand = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
newCommand, node)
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
result.createVar("future" & $node[0][0].ident, node[0][2][1],
newVarSection[0][2], newVarSection, node)
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
var newAsgn = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
else: discard
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await":
var newDiscard = node
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
newDiscard[0], newDiscard, node)
of nnkTryStmt:
# try: await x; except: ...
result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: expr) =
var temp = n
n[0] = tryBody
tryBody = temp
# Transform ``except`` body.
# TODO: Could we perform some ``await`` transformation here to get it
# working in ``except``?
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil)
proc processForTry(n: NimNode, i: var int,
res: NimNode): bool {.compileTime.} =
## Transforms the body of the tryStmt. Does not transform the
## body in ``except``.
## Returns true if the tryStmt node was transformed into an ifStmt.
result = false
var skipped = n.skipStmtList()
while i < skipped.len:
var processed = processBody(skipped[i], retFutureSym,
subTypeIsVoid, n)
# Check if we transformed the node into an exception check.
# This suggests skipped[i] contains ``await``.
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
processed = processed.skipUntilStmtList()
expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse)
i.inc
if not processForTry(n, i, processed[2][1][0]):
# We need to wrap the nnkElse nodes back into a tryStmt.
# As they are executed if an exception does not happen
# inside the awaited future.
# The following code will wrap the nodes inside the
# original tryStmt.
wrapInTry(n, processed[2][1][0])
res.add processed
result = true
else:
res.add skipped[i]
i.inc
var i = 0
if not processForTry(node, i, result):
# If the tryStmt hasn't been transformed we can just put the body
# back into it.
wrapInTry(node, result)
return
else: discard
for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil)
proc getName(node: NimNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
of nnkIdent:
return $node.ident
of nnkEmpty:
return "anonymous"
else:
error("Unknown name.")
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
## This macro transforms a single procedure into a closure iterator.
## The ``async`` macro supports a stmtList holding multiple async procedures.
if prc.kind notin {nnkProcDef, nnkLambda}:
error("Cannot transform this node kind into an async proc." &
" Proc definition or lambda node expected.")
hint("Processing " & prc[0].getName & " as an async proc.")
let returnType = prc[3][0]
var baseType: NimNode
# Verify that the return type is a Future[T]
if returnType.kind == nnkBracketExpr:
let fut = repr(returnType[0])
if fut != "Future":
error("Expected return type of 'Future' got '" & fut & "'")
baseType = returnType[1]
elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
let fut = repr(returnType[1])
if fut != "Future":
error("Expected return type of 'Future' got '" & fut & "'")
baseType = returnType[2]
elif returnType.kind == nnkEmpty:
baseType = returnType
else:
error("Expected return type of 'Future' got '" & repr(returnType) & "'")
let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].ident == !"void")
var outerProcBody = newNimNode(nnkStmtList, prc[6])
# -> var retFuture = newFuture[T]()
var retFutureSym = genSym(nskVar, "retFuture")
var subRetType =
if returnType.kind == nnkEmpty: newIdentNode("void")
else: baseType
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc[6]).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
else:
# -> complete(retFuture)
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
# -> createCb(retFuture)
#var cbName = newIdentNode("cb")
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName))
outerProcBody.add procCb
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
result[4].del(i)
result[4] = newEmptyNode()
if subtypeIsVoid:
# Add discardable pragma.
if returnType.kind == nnkEmpty:
# Add Future[void]
result[3][0] = parseExpr("Future[void]")
if procBody.kind != nnkEmpty:
result[6] = outerProcBody
#echo(treeRepr(result))
#if prc[0].getName == "testInfix":
# echo(toStrLit(result))
macro async*(prc: untyped): untyped =
## Macro which processes async procedures into the appropriate
## iterators and yield statements.
if prc.kind == nnkStmtList:
for oneProc in prc:
result = newStmtList()
result.add asyncSingleProc(oneProc)
else:
result = asyncSingleProc(prc)
when defined(nimDumpAsync):
echo repr result
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs. ## a full line is read or an error occurs.
## ##
@ -1955,6 +1384,8 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
## ##
## **Note**: This procedure is mostly used for testing. You likely want to ## **Note**: This procedure is mostly used for testing. You likely want to
## use ``asyncnet.recvLine`` instead. ## use ``asyncnet.recvLine`` instead.
##
## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
template addNLIfEmpty(): stmt = template addNLIfEmpty(): stmt =
if result.len == 0: if result.len == 0:

View file

@ -24,6 +24,8 @@
import asyncdispatch, os import asyncdispatch, os
# TODO: Fix duplication introduced by PR #4683.
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
import winlean import winlean
else: else:
@ -112,6 +114,80 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
register(result.fd) register(result.fd)
proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
## Read ``size`` bytes from the specified file asynchronously starting at
## the current position of the file pointer.
##
## If the file pointer is past the end of the file then an empty string is
## returned.
var retFuture = newFuture[int]("asyncfile.readBuffer")
when defined(windows) or defined(nimdoc):
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: f.fd, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount > 0
assert bytesCount <= size
f.offset.inc bytesCount
retFuture.complete(bytesCount)
else:
if errcode.int32 == ERROR_HANDLE_EOF:
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
ol.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
let ret = readFile(f.fd.Handle, buf, size.int32, nil,
cast[POVERLAPPED](ol))
if not ret.bool:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err)))
else:
# Request completed immediately.
var bytesRead: DWord
let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol), bytesRead, false.WinBool)
if not overlappedRes.bool:
let err = osLastError()
if err.int32 == ERROR_HANDLE_EOF:
retFuture.complete(0)
else:
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else:
assert bytesRead > 0
assert bytesRead <= size
f.offset.inc bytesRead
retFuture.complete(bytesRead)
else:
proc cb(fd: AsyncFD): bool =
result = true
let res = read(fd.cint, cast[cstring](buf), size.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 != EAGAIN:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
elif res == 0:
# EOF
retFuture.complete(0)
else:
f.offset.inc(res)
retFuture.complete(res)
if not cb(f.fd):
addRead(f.fd, cb)
return retFuture
proc read*(f: AsyncFile, size: int): Future[string] = proc read*(f: AsyncFile, size: int): Future[string] =
## Read ``size`` bytes from the specified file asynchronously starting at ## Read ``size`` bytes from the specified file asynchronously starting at
## the current position of the file pointer. ## the current position of the file pointer.
@ -238,6 +314,73 @@ proc readAll*(f: AsyncFile): Future[string] {.async.} =
return return
result.add data result.add data
proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
## Writes ``size`` bytes from ``buf`` to the file specified asynchronously.
##
## The returned Future will complete once all data has been written to the
## specified file.
var retFuture = newFuture[void]("asyncfile.writeBuffer")
when defined(windows) or defined(nimdoc):
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: f.fd, cb:
proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount == size.int32
f.offset.inc(size)
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
ol.offset = DWord(f.offset and 0xffffffff)
ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
cast[POVERLAPPED](ol))
if not ret.bool:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err)))
else:
# Request completed immediately.
var bytesWritten: DWord
let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol), bytesWritten, false.WinBool)
if not overlappedRes.bool:
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
else:
assert bytesWritten == size.int32
f.offset.inc(size)
retFuture.complete()
else:
var written = 0
proc cb(fd: AsyncFD): bool =
result = true
let remainderSize = size-written
var cbuf = cast[cstring](buf)
let res = write(fd.cint, addr cbuf[written], remainderSize.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 != EAGAIN:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
written.inc res
f.offset.inc res
if res != remainderSize:
result = false # We still have data to write.
else:
retFuture.complete()
if not cb(f.fd):
addWrite(f.fd, cb)
return retFuture
proc write*(f: AsyncFile, data: string): Future[void] = proc write*(f: AsyncFile, data: string): Future[void] =
## Writes ``data`` to the file specified asynchronously. ## Writes ``data`` to the file specified asynchronously.
## ##

View file

@ -281,7 +281,7 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
assertReply(await(ftp.expectReply()), "226") assertReply(await(ftp.expectReply()), "226")
proc defaultOnProgressChanged*(total, progress: BiggestInt, proc defaultOnProgressChanged*(total, progress: BiggestInt,
speed: float): Future[void] {.nimcall,gcsafe.} = speed: float): Future[void] {.nimcall,gcsafe,procvar.} =
## Default FTP ``onProgressChanged`` handler. Does nothing. ## Default FTP ``onProgressChanged`` handler. Does nothing.
result = newFuture[void]() result = newFuture[void]()
#echo(total, " ", progress, " ", speed) #echo(total, " ", progress, " ", speed)
@ -354,6 +354,20 @@ proc store*(ftp: AsyncFtpClient, file, dest: string,
await doUpload(ftp, destFile, onProgressChanged) await doUpload(ftp, destFile, onProgressChanged)
proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
## Rename a file or directory on the remote FTP Server from current name
## ``name_from`` to new name ``name_to``
assertReply(await ftp.send("RNFR " & name_from), "350")
assertReply(await ftp.send("RNTO " & name_to), "250")
proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} =
## Delete a file ``filename`` on the remote FTP server
assertReply(await ftp.send("DELE " & filename), "250")
proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
## Delete a directory ``dir`` on the remote FTP server
assertReply(await ftp.send("RMD " & dir), "250")
proc newAsyncFtpClient*(address: string, port = Port(21), proc newAsyncFtpClient*(address: string, port = Port(21),
user, pass = ""): AsyncFtpClient = user, pass = ""): AsyncFtpClient =
## Creates a new ``AsyncFtpClient`` object. ## Creates a new ``AsyncFtpClient`` object.
@ -373,6 +387,11 @@ when not defined(testing) and isMainModule:
echo await ftp.listDirs() echo await ftp.listDirs()
await ftp.store("payload.jpg", "payload.jpg") await ftp.store("payload.jpg", "payload.jpg")
await ftp.retrFile("payload.jpg", "payload2.jpg") await ftp.retrFile("payload.jpg", "payload2.jpg")
await ftp.rename("payload.jpg", "payload_renamed.jpg")
await ftp.store("payload.jpg", "payload_remove.jpg")
await ftp.removeFile("payload_remove.jpg")
await ftp.createDir("deleteme")
await ftp.removeDir("deleteme")
echo("Finished") echo("Finished")
waitFor main(ftp) waitFor main(ftp)

View file

@ -9,6 +9,12 @@
## This module implements a high performance asynchronous HTTP server. ## This module implements a high performance asynchronous HTTP server.
## ##
## This HTTP server has not been designed to be used in production, but
## for testing applications locally. Because of this, when deploying your
## application you should use a reverse proxy (for example nginx) instead of
## allowing users to connect directly to this server.
##
##
## Examples ## Examples
## -------- ## --------
## ##
@ -38,7 +44,7 @@ export httpcore except parseHeader
type type
Request* = object Request* = object
client*: AsyncSocket # TODO: Separate this into a Response object? client*: AsyncSocket # TODO: Separate this into a Response object?
reqMethod*: string reqMethod*: HttpMethod
headers*: HttpHeaders headers*: HttpHeaders
protocol*: tuple[orig: string, major, minor: int] protocol*: tuple[orig: string, major, minor: int]
url*: Uri url*: Uri
@ -127,7 +133,14 @@ proc processClient(client: AsyncSocket, address: string,
var i = 0 var i = 0
for linePart in lineFut.mget.split(' '): for linePart in lineFut.mget.split(' '):
case i case i
of 0: request.reqMethod.shallowCopy(linePart.normalize) of 0:
try:
# TODO: this is likely slow.
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
except ValueError:
asyncCheck request.respond(Http400, "Invalid request method. Got: " &
linePart)
continue
of 1: parseUri(linePart, request.url) of 1: parseUri(linePart, request.url)
of 2: of 2:
try: try:
@ -159,7 +172,7 @@ proc processClient(client: AsyncSocket, address: string,
request.client.close() request.client.close()
return return
if request.reqMethod == "post": if request.reqMethod == HttpPost:
# Check for Expect header # Check for Expect header
if request.headers.hasKey("Expect"): if request.headers.hasKey("Expect"):
if "100-continue" in request.headers["Expect"]: if "100-continue" in request.headers["Expect"]:
@ -178,17 +191,12 @@ proc processClient(client: AsyncSocket, address: string,
else: else:
request.body = await client.recv(contentLength) request.body = await client.recv(contentLength)
assert request.body.len == contentLength assert request.body.len == contentLength
elif request.reqMethod == "post": elif request.reqMethod == HttpPost:
await request.respond(Http400, "Bad Request. No Content-Length.") await request.respond(Http400, "Bad Request. No Content-Length.")
continue continue
case request.reqMethod # Call the user's callback.
of "get", "post", "head", "put", "delete", "trace", "options", await callback(request)
"connect", "patch":
await callback(request)
else:
await request.respond(Http400, "Invalid request method. Got: " &
request.reqMethod)
if "upgrade" in request.headers.getOrDefault("connection"): if "upgrade" in request.headers.getOrDefault("connection"):
return return

515
lib/pure/asyncmacro.nim Normal file
View file

@ -0,0 +1,515 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## AsyncMacro
## *************
## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
import macros, strutils
proc skipUntilStmtList(node: NimNode): NimNode {.compileTime.} =
# Skips a nest of StmtList's.
result = node
if node[0].kind == nnkStmtList:
result = skipUntilStmtList(node[0])
proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
result = node
if node[0].kind == nnkStmtList:
result = node[0]
template createCb(retFutureSym, iteratorNameSym,
name, futureVarCompletions: untyped) =
var nameIterVar = iteratorNameSym
#{.push stackTrace: off.}
proc cb {.closure,gcsafe.} =
try:
if not nameIterVar.finished:
var next = nameIterVar()
if next == nil:
assert retFutureSym.finished, "Async procedure's (" &
name & ") return Future was not finished."
else:
next.callback = cb
except:
if retFutureSym.finished:
# Take a look at tasyncexceptions for the bug which this fixes.
# That test explains it better than I can here.
raise
else:
retFutureSym.fail(getCurrentException())
futureVarCompletions
cb()
#{.pop.}
proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
if tryStmt.kind == nnkNilLit:
result = rootReceiver
else:
var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
let errorNode = newDotExpr(futSym, newIdentNode("error"))
for i in 1 .. <tryStmt.len:
let exceptBranch = tryStmt[i]
if exceptBranch[0].kind == nnkStmtList:
exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
else:
var exceptIdentCount = 0
var ifCond: NimNode
for i in 0 .. <exceptBranch.len:
let child = exceptBranch[i]
if child.kind == nnkIdent:
let cond = infix(errorNode, "of", child)
if exceptIdentCount == 0:
ifCond = cond
else:
ifCond = infix(ifCond, "or", cond)
else:
break
exceptIdentCount.inc
expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
# -> -> else: raise futSym.error
exceptionChecks.add((newIdentNode("true"),
newNimNode(nnkRaiseStmt).add(errorNode)))
# Read the future if there is no error.
# -> else: futSym.read
let elseNode = newNimNode(nnkElse, fromNode)
elseNode.add newNimNode(nnkStmtList, fromNode)
elseNode[0].add rootReceiver
let ifBody = newStmtList()
ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
ifBody.add newIfStmt(exceptionChecks)
ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
result = newIfStmt(
(newDotExpr(futSym, newIdentNode("failed")), ifBody)
)
result.add elseNode
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
rootReceiver: expr, fromNode: NimNode) =
## Params:
## futureVarNode: The NimNode which is a symbol identifying the Future[T]
## variable to yield.
## fromNode: Used for better debug information (to give context).
## valueReceiver: The node which defines an expression that retrieves the
## future's value.
##
## rootReceiver: ??? TODO
# -> yield future<x>
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver,
fromNode)
template createVar(result: var NimNode, futSymName: string,
asyncProc: NimNode,
valueReceiver, rootReceiver: expr,
fromNode: NimNode) =
result = newNimNode(nnkStmtList, fromNode)
var futSym = genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
proc createFutureVarCompletions(futureVarIdents: seq[NimNode]): NimNode
{.compileTime.} =
result = newStmtList()
# Add calls to complete each FutureVar parameter.
for ident in futureVarIdents:
# Only complete them if they have not been completed already by the user.
result.add newIfStmt(
(
newCall(newIdentNode("not"),
newDotExpr(ident, newIdentNode("finished"))),
newCall(newIdentNode("complete"), ident)
)
)
proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool, futureVarIdents: seq[NimNode],
tryStmt: NimNode): NimNode {.compileTime.} =
#echo(node.treeRepr)
result = node
case node.kind
of nnkReturnStmt:
result = newNimNode(nnkStmtList, node)
if node[0].kind == nnkEmpty:
if not subTypeIsVoid:
result.add newCall(newIdentNode("complete"), retFutureSym,
newIdentNode("result"))
else:
result.add newCall(newIdentNode("complete"), retFutureSym)
else:
let x = node[0].processBody(retFutureSym, subTypeIsVoid,
futureVarIdents, tryStmt)
if x.kind == nnkYieldStmt: result.add x
else:
result.add newCall(newIdentNode("complete"), retFutureSym, x)
result.add createFutureVarCompletions(futureVarIdents)
result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
return # Don't process the children of this return stmt
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].ident == !"await":
case node[1].kind
of nnkIdent, nnkInfix, nnkDotExpr, nnkCall, nnkCommand:
# await x
# await x or y
# await foo(p, x)
# await foo p, x
var futureValue: NimNode
result.createVar("future" & $node[1][0].toStrLit, node[1], futureValue,
futureValue, node)
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
# foo await x
var newCommand = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1],
newCommand, node)
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
result.createVar("future" & $node[0][0].ident, node[0][2][1],
newVarSection[0][2], newVarSection, node)
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
var newAsgn = node
result.createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1], newAsgn, node)
else: discard
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await":
var newDiscard = node
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
newDiscard[0], newDiscard, node)
of nnkTryStmt:
# try: await x; except: ...
result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: expr) =
var temp = n
n[0] = tryBody
tryBody = temp
# Transform ``except`` body.
# TODO: Could we perform some ``await`` transformation here to get it
# working in ``except``?
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid,
futureVarIdents, nil)
proc processForTry(n: NimNode, i: var int,
res: NimNode): bool {.compileTime.} =
## Transforms the body of the tryStmt. Does not transform the
## body in ``except``.
## Returns true if the tryStmt node was transformed into an ifStmt.
result = false
var skipped = n.skipStmtList()
while i < skipped.len:
var processed = processBody(skipped[i], retFutureSym,
subTypeIsVoid, futureVarIdents, n)
# Check if we transformed the node into an exception check.
# This suggests skipped[i] contains ``await``.
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
processed = processed.skipUntilStmtList()
expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse)
i.inc
if not processForTry(n, i, processed[2][1][0]):
# We need to wrap the nnkElse nodes back into a tryStmt.
# As they are executed if an exception does not happen
# inside the awaited future.
# The following code will wrap the nodes inside the
# original tryStmt.
wrapInTry(n, processed[2][1][0])
res.add processed
result = true
else:
res.add skipped[i]
i.inc
var i = 0
if not processForTry(node, i, result):
# If the tryStmt hasn't been transformed we can just put the body
# back into it.
wrapInTry(node, result)
return
else: discard
for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
futureVarIdents, nil)
proc getName(node: NimNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
of nnkIdent:
return $node.ident
of nnkEmpty:
return "anonymous"
else:
error("Unknown name.")
proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
result = @[]
for i in 1 .. <len(params):
expectKind(params[i], nnkIdentDefs)
if params[i][1].kind == nnkBracketExpr and
($params[i][1][0].ident).normalize == "futurevar":
result.add(params[i][0])
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
## This macro transforms a single procedure into a closure iterator.
## The ``async`` macro supports a stmtList holding multiple async procedures.
if prc.kind notin {nnkProcDef, nnkLambda}:
error("Cannot transform this node kind into an async proc." &
" Proc definition or lambda node expected.")
hint("Processing " & prc[0].getName & " as an async proc.")
let returnType = prc[3][0]
var baseType: NimNode
# Verify that the return type is a Future[T]
if returnType.kind == nnkBracketExpr:
let fut = repr(returnType[0])
if fut != "Future":
error("Expected return type of 'Future' got '" & fut & "'")
baseType = returnType[1]
elif returnType.kind in nnkCallKinds and $returnType[0] == "[]":
let fut = repr(returnType[1])
if fut != "Future":
error("Expected return type of 'Future' got '" & fut & "'")
baseType = returnType[2]
elif returnType.kind == nnkEmpty:
baseType = returnType
else:
error("Expected return type of 'Future' got '" & repr(returnType) & "'")
let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].ident == !"void")
let futureVarIdents = getFutureVarIdents(prc[3])
var outerProcBody = newNimNode(nnkStmtList, prc[6])
# -> var retFuture = newFuture[T]()
var retFutureSym = genSym(nskVar, "retFuture")
var subRetType =
if returnType.kind == nnkEmpty: newIdentNode("void")
else: baseType
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr, prc[6]).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid,
futureVarIdents, nil)
# don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty:
if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
else:
# -> complete(retFuture)
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
procBody.add(createFutureVarCompletions(futureVarIdents))
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
# -> createCb(retFuture)
#var cbName = newIdentNode("cb")
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName),
createFutureVarCompletions(futureVarIdents))
outerProcBody.add procCb
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
result[4].del(i)
result[4] = newEmptyNode()
if subtypeIsVoid:
# Add discardable pragma.
if returnType.kind == nnkEmpty:
# Add Future[void]
result[3][0] = parseExpr("Future[void]")
if procBody.kind != nnkEmpty:
result[6] = outerProcBody
#echo(treeRepr(result))
#if prc[0].getName == "testInfix":
# echo(toStrLit(result))
macro async*(prc: untyped): untyped =
## Macro which processes async procedures into the appropriate
## iterators and yield statements.
if prc.kind == nnkStmtList:
for oneProc in prc:
result = newStmtList()
result.add asyncSingleProc(oneProc)
else:
result = asyncSingleProc(prc)
when defined(nimDumpAsync):
echo repr result
# Multisync
proc emptyNoop[T](x: T): T =
# The ``await``s are replaced by a call to this for simplicity.
when T isnot void:
return x
proc stripAwait(node: NimNode): NimNode =
## Strips out all ``await`` commands from a procedure body, replaces them
## with ``emptyNoop`` for simplicity.
result = node
let emptyNoopSym = bindSym("emptyNoop")
case node.kind
of nnkCommand, nnkCall:
if node[0].kind == nnkIdent and node[0].ident == !"await":
node[0] = emptyNoopSym
elif node.len > 1 and node[1].kind == nnkCommand and
node[1][0].kind == nnkIdent and node[1][0].ident == !"await":
# foo await x
node[1][0] = emptyNoopSym
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].kind == nnkIdent and node[0][2][0].ident == !"await":
# var x = await y
node[0][2][0] = emptyNoopSym
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
node[1][0] = emptyNoopSym
else: discard
of nnkDiscardStmt:
# discard await x
if node[0].kind == nnkCommand and node[0][0].kind == nnkIdent and
node[0][0].ident == !"await":
node[0][0] = emptyNoopSym
else: discard
for i in 0 .. <result.len:
result[i] = stripAwait(result[i])
proc splitParams(param: NimNode, async: bool): NimNode =
expectKind(param, nnkIdentDefs)
result = param
if param[1].kind == nnkInfix and $param[1][0].ident in ["|", "or"]:
let firstType = param[1][1]
let firstTypeName = $firstType.ident
let secondType = param[1][2]
let secondTypeName = $secondType.ident
# Make sure that at least one has the name `async`, otherwise we shouldn't
# touch it.
if not ("async" in firstTypeName.normalize or
"async" in secondTypeName.normalize):
return
if async:
if firstTypeName.normalize.startsWith("async"):
result = newIdentDefs(param[0], param[1][1])
elif secondTypeName.normalize.startsWith("async"):
result = newIdentDefs(param[0], param[1][2])
else:
if not firstTypeName.normalize.startsWith("async"):
result = newIdentDefs(param[0], param[1][1])
elif not secondTypeName.normalize.startsWith("async"):
result = newIdentDefs(param[0], param[1][2])
proc stripReturnType(returnType: NimNode): NimNode =
# Strip out the 'Future' from 'Future[T]'.
result = returnType
if returnType.kind == nnkBracketExpr:
let fut = repr(returnType[0])
if fut != "Future":
error("Expected return type of 'Future' got '" & fut & "'")
result = returnType[1]
proc splitProc(prc: NimNode): (NimNode, NimNode) =
## Takes a procedure definition which takes a generic union of arguments,
## for example: proc (socket: Socket | AsyncSocket).
## It transforms them so that ``proc (socket: Socket)`` and
## ``proc (socket: AsyncSocket)`` are returned.
result[0] = prc.copyNimTree()
result[0][3][0] = stripReturnType(result[0][3][0])
for i in 1 .. <result[0][3].len:
result[0][3][i] = splitParams(result[0][3][i], false)
result[0][6] = stripAwait(result[0][6])
result[1] = prc.copyNimTree()
for i in 1 .. <result[1][3].len:
result[1][3][i] = splitParams(result[1][3][i], true)
macro multisync*(prc: untyped): untyped =
## Macro which processes async procedures into both asynchronous and
## synchronous procedures.
##
## The generated async procedures use the ``async`` macro, whereas the
## generated synchronous procedures simply strip off the ``await`` calls.
hint("Processing " & prc[0].getName & " as a multisync proc.")
let (sync, asyncPrc) = splitProc(prc)
result = newStmtList()
result.add(asyncSingleProc(asyncPrc))
result.add(sync)

Some files were not shown because too many files have changed in this diff Show more