Merge pull request #1 from nim-lang/devel

pull-23-04-18
This commit is contained in:
Michael Voronin
2018-04-23 11:35:56 +03:00
committed by GitHub
81 changed files with 1074 additions and 1398 deletions

View File

@@ -11,6 +11,10 @@
to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``.
- Assignments that would "slice" an object into its supertype are not prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue.
#### Breaking changes in the standard library
@@ -27,6 +31,7 @@
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
to ``times.Duration`` in order to support higher time resolutions.
The proc is no longer deprecated.
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
#### Breaking changes in the compiler

View File

@@ -1042,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode =
new(result)
result.kind = kind
#result.info = UnknownLineInfo() inlined:
result.info.fileIndex = int32(-1)
result.info.fileIndex = InvalidFileIdx
result.info.col = int16(-1)
result.info.line = int16(-1)
result.info.line = uint16(0)
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
@@ -1116,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
s.typ = t
result = s
template fileIdx*(c: PSym): int32 =
template fileIdx*(c: PSym): FileIndex =
# XXX: this should be used only on module symbols
c.position.int32
c.position.FileIndex
template filename*(c: PSym): string =
# XXX: this should be used only on module symbols
c.position.int32.toFilename
c.position.FileIndex.toFilename
proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be

View File

@@ -72,24 +72,25 @@ proc bitSetContains(x, y: TBitSet): bool =
result = true
# Number of set bits for all values of int8
const populationCount: array[low(int8)..high(int8), int8] = [
1.int8, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
]
const populationCount: array[low(int8)..high(int8), int8] = block:
var arr: array[low(int8)..high(int8), int8]
proc countSetBits(x: int8): int8 =
return
( x and 0b00000001'i8) +
((x and 0b00000010'i8) shr 1) +
((x and 0b00000100'i8) shr 2) +
((x and 0b00001000'i8) shr 3) +
((x and 0b00010000'i8) shr 4) +
((x and 0b00100000'i8) shr 5) +
((x and 0b01000000'i8) shr 6) +
((x and 0b10000000'i8) shr 7)
for it in low(int8)..high(int8):
arr[it] = countSetBits(it)
arr
proc bitSetCard(x: TBitSet): BiggestInt =
for it in x:

View File

@@ -2023,7 +2023,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} =
let theMacro = it[0].sym
add p.s(cpsStmts), initFrameNoDebug(p, frameName,
makeCString theMacro.name.s,
theMacro.info.quotedFilename, it.info.line)
theMacro.info.quotedFilename, it.info.line.int)
else:
genStmts(p, it)
if n.len > 0: exprOrStmt

View File

@@ -223,7 +223,7 @@ proc genLineDir(p: BProc, t: PNode) =
line.rope, makeCString(toFilename(tt.info)))
elif ({optLineTrace, optStackTrace} * p.options ==
{optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0:
(p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
if freshLineInfo(p, tt.info):
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
line.rope, tt.info.quotedFilename)
@@ -678,7 +678,7 @@ proc generateHeaders(m: BModule) =
proc openNamespaceNim(): Rope =
result.add("namespace Nim {" & tnl)
proc closeNamespaceNim(): Rope =
result.add("}" & tnl)
@@ -1090,7 +1090,7 @@ proc genMainProc(m: BModule) =
appcg(m, m.s[cfsProcs], nimMain,
[m.g.mainModInit, initStackBottomCall, rope(m.labels)])
if optNoMain notin gGlobalOptions:
if useNimNamespace:
if useNimNamespace:
m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl
appcg(m, m.s[cfsProcs], otherMain, [])
@@ -1202,7 +1202,7 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
add(result, genSectionStart(i))
add(result, m.s[i])
add(result, genSectionEnd(i))
if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim()
if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim()
add(result, m.s[cfsInitProc])
if useNimNamespace: result.add closeNamespaceNim()
@@ -1301,7 +1301,7 @@ proc resetCgenModules*(g: BModuleList) =
for m in cgenModules(g): resetModule(m)
proc rawNewModule(g: BModuleList; module: PSym): BModule =
result = rawNewModule(g, module, module.position.int32.toFullPath)
result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
proc newModule(g: BModuleList; module: PSym): BModule =
# we should create only one cgen module for each module sym
@@ -1311,7 +1311,7 @@ proc newModule(g: BModuleList; module: PSym): BModule =
if (optDeadCodeElim in gGlobalOptions):
if (sfDeadCodeElim in module.flags):
internalError("added pending module twice: " & module.filename)
internalError("added pending module twice: " & toFilename(FileIndex module.position))
template injectG(config) {.dirty.} =
if graph.backend == nil:

View File

@@ -77,6 +77,14 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
{msgStdout})
msgQuit(0)
proc writeFullhelp(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & Usage & AdvancedUsage,
{msgStdout})
msgQuit(0)
proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString,
@@ -311,7 +319,7 @@ proc trackDirty(arg: string, info: TLineInfo) =
localError(info, errInvalidNumber, a[2])
let dirtyOriginalIdx = a[1].fileInfoIdx
if dirtyOriginalIdx >= 0:
if dirtyOriginalIdx.int32 >= 0:
msgs.setDirtyFile(dirtyOriginalIdx, a[0])
gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
@@ -611,6 +619,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "advanced":
expectNoArg(switch, arg, pass, info)
writeAdvancedUsage(pass)
of "fullhelp":
expectNoArg(switch, arg, pass, info)
writeFullhelp(pass)
of "help", "h":
expectNoArg(switch, arg, pass, info)
helpOnError(pass)
@@ -702,7 +713,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(switch, arg, pass, info)
useNimNamespace = true
defineSymbol("cppCompileToNamespace")
else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info)

View File

@@ -544,7 +544,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k), "line": %n.info.line,
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col}
if comm != nil and comm != "":
result["description"] = %comm
@@ -618,7 +618,7 @@ proc generateJson*(d: PDoc, n: PNode) =
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line,
d.add %{ "comment": %stripped, "line": %n.info.line.int,
"col": %n.info.col }
of nkProcDef:
when useEffectSystem: documentRaises(n)
@@ -790,7 +790,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
discard "fixme: error report"
proc commandDoc*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
@@ -840,7 +840,7 @@ proc commandRst2TeX*() =
commandRstAux(gProjectFull, TexExt)
proc commandJson*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
@@ -855,7 +855,7 @@ proc commandJson*() =
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc commandTags*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache())
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true

View File

@@ -106,8 +106,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i]
# to prevent endless recursion in template instantiation
const evalTemplateLimit* = 1000
var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation
proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
when true:
@@ -133,7 +134,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
if evalTemplateCounter > evalTemplateLimit:
globalError(n.info, errTemplateInstantiationTooNested)
result = n

View File

@@ -35,7 +35,7 @@ const
proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false
@@ -212,9 +212,9 @@ proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
p.x = newStringOfCap(120)
# do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1)
p.info.line = p.info.line + 1'u16
while llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1)
p.info.line = p.info.line + 1'u16
parseLine(p)
newLine(p)
result = p.outp

View File

@@ -44,7 +44,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
assert x.kind in {nkStmtList, nkCall}
# better be safe than sorry, so check evalTemplateCounter too:
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
if evalTemplateCounter > evalTemplateLimit:
globalError(n.info, errTemplateInstantiationTooNested)
# deactivate this pattern:
c.patterns[i] = nil

File diff suppressed because it is too large Load Diff

View File

@@ -121,26 +121,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfoPHP(p: PProc; t: PType): Rope =
let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "$$NTI$1" % [rope(t.id)]
p.declareGlobal(t.id, result)
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
let length = sonsLen(t.n)
var s: Rope = nil
for i in countup(0, length - 1):
if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo")
let field = t.n.sons[i].sym
if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
addf(s, "$# => $#$n",
[rope(field.position), makeJSString(extName)])
prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s])
proc genTypeInfo(p: PProc, typ: PType): Rope =
if p.target == targetPHP:
return makeJSString(typeToString(typ, preferModuleInfo))
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return

View File

@@ -230,7 +230,7 @@ template isIterator*(owner: PSym): bool =
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
result = gCmd == cmdCompileToJS and not isCompileTime
proc liftIterSym*(n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
@@ -813,7 +813,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (
gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or
gCmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule:
# ignore forward declaration:
result = body

View File

@@ -133,7 +133,7 @@ type
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
TLexer* = object of TBaseLexer
fileIdx*: int32
fileIdx*: FileIndex
indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments
# needs so much look-ahead
@@ -222,7 +222,7 @@ proc fillToken(L: var TToken) =
L.commentOffsetA = 0
L.commentOffsetB = 0
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache) =
openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx
@@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} =
when defined(nimsuggest):
let colB = getColNumber(L, pos)+1
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.InToken
gTrackPos.col = colA.int16
colA = 0
@@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) =
when defined(nimsuggest):
let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
gTrackPos.fileIndex = trackPosInvalidFileIdx
gTrackPos.line = -1
gTrackPos.line = 0'u16
colA = 0
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
@@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) =
# the cursor in a string literal or comment:
let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.BeforeToken
gTrackPos = L.previousToken
gTrackPosAttached = true
@@ -1003,6 +1003,10 @@ proc skip(L: var TLexer, tok: var TToken) =
var buf = L.buf
tokenBegin(tok, pos)
tok.strongSpaceA = 0
when defined(nimpretty):
var hasComment = false
tok.commentOffsetA = L.offsetBase + pos
tok.commentOffsetB = tok.commentOffsetA
while true:
case buf[pos]
of ' ':
@@ -1021,6 +1025,7 @@ proc skip(L: var TLexer, tok: var TToken) =
inc(pos)
inc(indent)
elif buf[pos] == '#' and buf[pos+1] == '[':
when defined(nimpretty): hasComment = true
skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos
buf = L.buf
@@ -1034,14 +1039,11 @@ proc skip(L: var TLexer, tok: var TToken) =
of '#':
# do not skip documentation comment:
if buf[pos+1] == '#': break
when defined(nimpretty):
tok.commentOffsetA = L.offsetBase + pos
when defined(nimpretty): hasComment = true
if buf[pos+1] == '[':
skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos
buf = L.buf
when defined(nimpretty):
tok.commentOffsetB = L.offsetBase + pos
else:
tokenBegin(tok, pos)
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
@@ -1053,6 +1055,9 @@ proc skip(L: var TLexer, tok: var TToken) =
tokenEndPrevious(tok, pos-1)
L.bufpos = pos
when defined(nimpretty):
if hasComment:
tok.commentOffsetB = L.offsetBase + pos
tok.tokType = tkComment
if gIndentationWidth <= 0:
gIndentationWidth = tok.indent
@@ -1061,7 +1066,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
when defined(nimsuggest):
# we attach the cursor to the last *strong* token
if tok.tokType notin weakTokens:
L.previousToken.line = tok.line.int16
L.previousToken.line = tok.line.uint16
L.previousToken.col = tok.col.int16
when defined(nimsuggest):
@@ -1074,6 +1079,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.indent = -1
skip(L, tok)
when defined(nimpretty):
if tok.tokType == tkComment:
L.indentAhead = L.currLineIndent
return
var c = L.buf[L.bufpos]
tok.line = L.lineNumber
tok.col = getColNumber(L, L.bufpos)
@@ -1109,7 +1118,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
tok.tokType = tkParLe
when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideCon:
tok.line == gTrackPos.line.int and gIdeCmd == ideCon:
gTrackPos.col = tok.col.int16
of ')':
tok.tokType = tkParRi
@@ -1130,7 +1139,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
of '.':
when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideSug:
tok.line == gTrackPos.line.int and gIdeCmd == ideSug:
tok.tokType = tkDot
L.cursor = CursorPosition.InToken
gTrackPos.col = tok.col.int16

View File

@@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
let f = open(changeFileExt(project, "deps"), fmWrite)
for m in g.modules:
if m != nil:
f.writeLine(toFullPath(m.position.int32))
f.writeLine(toFullPath(m.position.FileIndex))
for k in g.inclToMod.keys:
if g.getModule(k).isNil: # don't repeat includes which are also modules
f.writeLine(k.toFullPath)
@@ -94,7 +94,6 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
defineSymbol("nimrod") # 'nimrod' is always defined
defineSymbol("ecmascript") # For backward compatibility
defineSymbol("js")
if gCmd == cmdCompileToPHP: defineSymbol("nimphp")
semanticPasses()
registerPass(JSgenPass)
compileProject(graph, cache)
@@ -189,9 +188,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "js", "compiletojs":
gCmd = cmdCompileToJS
commandCompileToJS(graph, cache)
of "php":
gCmd = cmdCompileToPHP
commandCompileToJS(graph, cache)
of "doc0":
wantMainModule()
gCmd = cmdDoc
@@ -269,7 +265,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
of "parse":
gCmd = cmdParse
wantMainModule()
discard parseFile(gProjectMainIdx, cache)
discard parseFile(FileIndex gProjectMainIdx, cache)
of "scan":
gCmd = cmdScan
wantMainModule()

View File

@@ -25,7 +25,7 @@
## - Its dependent module stays the same.
##
import ast, intsets, tables, options, rod
import ast, intsets, tables, options, rod, msgs, hashes
type
ModuleGraph* = ref object
@@ -34,10 +34,10 @@ type
deps*: IntSet # the dependency graph or potentially its transitive closure.
suggestMode*: bool # whether we are in nimsuggest mode or not.
invalidTransitiveClosure: bool
inclToMod*: Table[int32, int32] # mapping of include file to the
# first module that included it
importStack*: seq[int32] # The current import stack. Used for detecting recursive
# module dependencies.
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
# first module that included it
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
# module dependencies.
backend*: RootRef # minor hack so that a backend can extend this easily
config*: ConfigRef
doStopCompile*: proc(): bool {.closure.}
@@ -45,6 +45,8 @@ type
owners*: seq[PSym]
methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
proc hash*(x: FileIndex): Hash {.borrow.}
{.this: g.}
proc stopCompile*(g: ModuleGraph): bool {.inline.} =
@@ -56,7 +58,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
result.deps = initIntSet()
result.modules = @[]
result.importStack = @[]
result.inclToMod = initTable[int32, int32]()
result.inclToMod = initTable[FileIndex, FileIndex]()
if config.isNil:
result.config = newConfigRef()
else:
@@ -69,35 +71,35 @@ proc resetAllModules*(g: ModuleGraph) =
deps = initIntSet()
modules = @[]
importStack = @[]
inclToMod = initTable[int32, int32]()
inclToMod = initTable[FileIndex, FileIndex]()
usageSym = nil
owners = @[]
methods = @[]
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < modules.len:
result = modules[fileIdx]
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
result = modules[fileIdx.int32]
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
proc addDep*(g: ModuleGraph; m: PSym, dep: int32) =
assert m.position == m.info.fileIndex
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
assert m.position == m.info.fileIndex.int32
addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
if suggestMode:
deps.incl m.position.dependsOn(dep)
deps.incl m.position.dependsOn(dep.int)
# we compute the transitive closure later when quering the graph lazily.
# this improve efficiency quite a lot:
#invalidTransitiveClosure = true
proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) =
proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
addModuleDep(module, includeFile, isIncludeFile = true)
discard hasKeyOrPut(inclToMod, includeFile, module)
proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 =
proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first
## references this include file.
if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil:
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
result = fileIdx
else:
result = inclToMod.getOrDefault(fileIdx)
@@ -111,11 +113,11 @@ proc transitiveClosure(g: var IntSet; n: int) =
if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)):
g.incl i.dependsOn(j)
proc markDirty*(g: ModuleGraph; fileIdx: int32) =
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
let m = getModule fileIdx
if m != nil: incl m.flags, sfDirty
proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# we need to mark its dependent modules D as dirty right away because after
# nimsuggest is done with this module, the module's dirty flag will be
# cleared but D still needs to be remembered as 'dirty'.
@@ -126,7 +128,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
# every module that *depends* on this file is also dirty:
for i in 0i32..<modules.len.int32:
let m = modules[i]
if m != nil and deps.contains(i.dependsOn(fileIdx)):
if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty
proc isDirty*(g: ModuleGraph; m: PSym): bool =

View File

@@ -174,7 +174,7 @@ proc getModuleName*(n: PNode): string =
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
result = ""
proc checkModuleName*(n: PNode; doLocalError=true): int32 =
proc checkModuleName*(n: PNode; doLocalError=true): FileIndex =
# This returns the full canonical path for a given module import
let modulename = n.getModuleName
let fullPath = findModule(modulename, n.info.toFullPath)

View File

@@ -13,115 +13,10 @@ import
ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod
when false:
type
TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
TModuleInMemory* = object
hash*: SecureHash
deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile
hashStatus*: THashStatus
var
gCompiledModules: seq[PSym] = @[]
gMemCacheData*: seq[TModuleInMemory] = @[]
## XXX: we should implement recycling of file IDs
## if the user keeps renaming modules, the file IDs will keep growing
gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
proc hashChanged(fileIdx: int32): bool =
internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
template updateStatus =
gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
else: hashNotChanged
# echo "TESTING Hash: ", fileIdx.toFilename, " ", result
case gMemCacheData[fileIdx].hashStatus
of hashHasChanged:
result = true
of hashNotChanged:
result = false
of hashCached:
let newHash = secureHashFile(fileIdx.toFullPath)
result = newHash != gMemCacheData[fileIdx].hash
gMemCacheData[fileIdx].hash = newHash
updateStatus()
of hashNotTaken:
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
result = true
updateStatus()
proc doHash(fileIdx: int32) =
if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
# echo "FIRST Hash: ", fileIdx.ToFilename
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetModule*(module: PSym) =
let conflict = getModule(module.position.int32)
if conflict == nil: return
doAssert conflict == module
resetModule(module.position.int32)
initStrTable(module.tab)
proc resetAllModules* =
for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil:
resetModule(i.int32)
resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND"
proc resetAllModulesHard* =
resetPackageCache()
gCompiledModules.setLen 0
gMemCacheData.setLen 0
magicsys.resetSysTypes()
# XXX
#gOwners = @[]
proc checkDepMem(fileIdx: int32): TNeedRecompile =
template markDirty =
resetModule(fileIdx)
return Yes
if gFuzzyGraphChecking:
if gMemCacheData[fileIdx].needsRecompile != Maybe:
return gMemCacheData[fileIdx].needsRecompile
else:
# cycle detection: We claim that a cycle does no harm.
if gMemCacheData[fileIdx].needsRecompile == Probing:
return No
if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
markDirty()
if gMemCacheData[fileIdx].deps != nil:
gMemCacheData[fileIdx].needsRecompile = Probing
for dep in gMemCacheData[fileIdx].deps:
let d = checkDepMem(dep)
if d in {Yes, Recompiled}:
# echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
markDirty()
gMemCacheData[fileIdx].needsRecompile = No
return No
proc resetSystemArtifacts*() =
magicsys.resetSysTypes()
proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
# We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID.
new(result)
@@ -144,9 +39,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
graph.packageSyms.strTableAdd(packSym)
result.owner = packSym
result.position = fileIdx
result.position = int fileIdx
growCache graph.modules, fileIdx
growCache graph.modules, int fileIdx
graph.modules[result.position] = result
incl(result.flags, sfUsed)
@@ -158,7 +53,7 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
# strTableIncl() for error corrections:
discard strTableIncl(packSym.tab, result)
proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym =
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
result = graph.getModule(fileIdx)
if result == nil:
#growCache gMemCacheData, fileIdx
@@ -199,7 +94,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
else:
result = gCompiledModules[fileIdx]
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PSym {.procvar.} =
# this is called by the semantic checking phase
result = compileModule(graph, fileIdx, cache, {})
@@ -210,11 +105,11 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
else: ForeignPackageNotes
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache)
graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx)
graph.addIncludeDep(s.position.FileIndex, fileIdx)
proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
if magicsys.systemModule == nil:
@@ -224,16 +119,16 @@ proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
proc wantMainModule* =
if gProjectFull.len == 0:
fatal(gCmdLineInfo, errCommandExpectsFilename)
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
gProjectMainIdx = int32 addFileExt(gProjectFull, NimExt).fileInfoIdx
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
proc compileProject*(graph: ModuleGraph; cache: IdentCache;
projectFileIdx = -1'i32) =
projectFileIdx = InvalidFileIDX) =
wantMainModule()
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(gProjectMainIdx) else: projectFileIdx
graph.importStack.add projectFile
if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})

View File

@@ -10,21 +10,24 @@
import
options, strutils, os, tables, ropes, platform, terminal, macros
const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
type
TMsgKind* = enum
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated,
errXCompilerDoesNotSupportCpp, errStringLiteralExpected,
errStringLiteralExpected,
errIntLiteralExpected, errInvalidCharacterConstant,
errClosingTripleQuoteExpected, errClosingQuoteExpected,
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
errTabulatorsAreNotAllowed, errInvalidToken,
errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
errOperatorExpected, errTokenExpected,
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionExpected, errExceptionAlreadyHandled,
errExceptionAlreadyHandled,
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
errInvalidNumberOfYieldExpr, errCannotReturnExpr,
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
@@ -68,7 +71,7 @@ type
errWrongNumberOfArgumentsInCall,
errMissingGenericParamsForTemplate,
errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed,
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor,
@@ -87,7 +90,8 @@ type
errNoReturnTypeDeclared,
errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope,
errXNeedsParamObjectType,
errTemplateInstantiationTooNested, errInstantiationFrom,
errTemplateInstantiationTooNested, errMacroInstantiationTooNested,
errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified,
errXExpected,
@@ -144,7 +148,6 @@ const
errIllFormedAstX: "illformed AST: $1",
errCannotOpenFile: "cannot open \'$1\'",
errGenerated: "$1",
errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++",
errStringLiteralExpected: "string literal expected",
errIntLiteralExpected: "integer literal expected",
errInvalidCharacterConstant: "invalid character constant",
@@ -152,7 +155,6 @@ const
errClosingQuoteExpected: "closing \" expected",
errTabulatorsAreNotAllowed: "tabulators are not allowed",
errInvalidToken: "invalid token: $1",
errLineTooLong: "line too long",
errInvalidNumber: "$1 is not a valid number",
errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
errNumberOutOfRange: "number $1 out of valid range",
@@ -164,7 +166,6 @@ const
errInvalidModuleName: "invalid module name: '$1'",
errOperatorExpected: "operator expected, but found \'$1\'",
errTokenExpected: "\'$1\' expected",
errStringAfterIncludeExpected: "string after \'include\' expected",
errRecursiveDependencyX: "recursive dependency: \'$1\'",
errOnOrOffExpected: "\'on\' or \'off\' expected",
errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected",
@@ -174,7 +175,6 @@ const
errAtPopWithoutPush: "\'pop\' without a \'push\' pragma",
errEmptyAsm: "empty asm statement",
errInvalidIndentation: "invalid indentation",
errExceptionExpected: "exception expected",
errExceptionAlreadyHandled: "exception already handled",
errYieldNotAllowedHere: "'yield' only allowed in an iterator",
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
@@ -280,7 +280,6 @@ const
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar",
errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration",
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
errImplOfXexpected: "implementation of \'$1\' expected",
@@ -329,7 +328,8 @@ const
errInvalidCommandX: "invalid command: \'$1\'",
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",
errTemplateInstantiationTooNested: "template/macro instantiation too nested",
errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument",
@@ -495,15 +495,18 @@ type
# and parsed; usually 'nil' but is used
# for 'nimsuggest'
hash*: string # the checksum of the file
when defined(nimpretty):
fullContent*: string
FileIndex* = distinct int32
TLineInfo* = object # This is designed to be as small as possible,
# because it is used
# in syntax nodes. We save space here by using
# two int16 and an int32.
# On 64 bit and on 32 bit systems this is
# only 8 bytes.
line*, col*: int16
fileIndex*: int32
line*: uint16
col*: int16
fileIndex*: FileIndex
when defined(nimpretty):
offsetA*, offsetB*: int
commentOffsetA*, commentOffsetB*: int
@@ -517,6 +520,8 @@ type
ERecoverableError* = object of ValueError
ESuggestDone* = object of Exception
proc `==`*(a, b: FileIndex): bool {.borrow.}
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
@@ -541,14 +546,14 @@ const
{low(TNoteKind)..high(TNoteKind)}]
const
InvalidFileIDX* = int32(-1)
InvalidFileIDX* = FileIndex(-1)
var
ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting}
filenameToIndexTbl = initTable[string, int32]()
filenameToIndexTbl = initTable[string, FileIndex]()
fileInfos*: seq[TFileInfo] = @[]
systemFileIdx*: int32
systemFileIdx*: FileIndex
proc toCChar*(c: char): string =
case c
@@ -583,6 +588,18 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
result.quotedFullName = fullPath.makeCString
if optEmbedOrigSrc in gGlobalOptions or true:
result.lines = @[]
when defined(nimpretty):
if result.fullPath.len > 0:
try:
result.fullContent = readFile(result.fullPath)
except IOError:
#rawMessage(errCannotOpenFile, result.fullPath)
# XXX fixme
result.fullContent = ""
when defined(nimpretty):
proc fileSection*(fid: FileIndex; a, b: int): string =
substr(fileInfos[fid.int].fullContent, a, b)
proc fileInfoKnown*(filename: string): bool =
var
@@ -593,7 +610,7 @@ proc fileInfoKnown*(filename: string): bool =
canon = filename
result = filenameToIndexTbl.hasKey(canon)
proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex =
var
canon: string
pseudoPath = false
@@ -611,28 +628,28 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 =
result = filenameToIndexTbl[canon]
else:
isKnownFile = false
result = fileInfos.len.int32
result = fileInfos.len.FileIndex
fileInfos.add(newFileInfo(canon, if pseudoPath: filename
else: canon.shortenDir))
filenameToIndexTbl[canon] = result
proc fileInfoIdx*(filename: string): int32 =
proc fileInfoIdx*(filename: string): FileIndex =
var dummy: bool
result = fileInfoIdx(filename, dummy)
proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo =
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
result.fileIndex = fileInfoIdx
result.line = int16(line)
result.line = uint16(line)
result.col = int16(col)
proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} =
result = newLineInfo(filename.fileInfoIdx, line, col)
fileInfos.add(newFileInfo("", "command line"))
var gCmdLineInfo* = newLineInfo(int32(0), 1, 1)
var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
fileInfos.add(newFileInfo("", "compilation artifact"))
var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1)
var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
raise newException(ERecoverableError, msg)
@@ -648,9 +665,9 @@ var
gMainPackageNotes*: TNoteKinds = NotesVerbosity[1]
proc unknownLineInfo*(): TLineInfo =
result.line = int16(-1)
result.line = uint16(0)
result.col = int16(-1)
result.fileIndex = -1
result.fileIndex = InvalidFileIDX
type
Severity* {.pure.} = enum ## VS Code only supports these three
@@ -712,32 +729,32 @@ proc getInfoContext*(index: int): TLineInfo =
if i >=% L: result = unknownLineInfo()
else: result = msgContext[i]
template toFilename*(fileIdx: int32): string =
(if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath)
template toFilename*(fileIdx: FileIndex): string =
(if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath)
proc toFullPath*(fileIdx: int32): string =
if fileIdx < 0: result = "???"
else: result = fileInfos[fileIdx].fullPath
proc toFullPath*(fileIdx: FileIndex): string =
if fileIdx.int32 < 0: result = "???"
else: result = fileInfos[fileIdx.int32].fullPath
proc setDirtyFile*(fileIdx: int32; filename: string) =
assert fileIdx >= 0
fileInfos[fileIdx].dirtyFile = filename
proc setDirtyFile*(fileIdx: FileIndex; filename: string) =
assert fileIdx.int32 >= 0
fileInfos[fileIdx.int32].dirtyFile = filename
proc setHash*(fileIdx: int32; hash: string) =
assert fileIdx >= 0
shallowCopy(fileInfos[fileIdx].hash, hash)
proc setHash*(fileIdx: FileIndex; hash: string) =
assert fileIdx.int32 >= 0
shallowCopy(fileInfos[fileIdx.int32].hash, hash)
proc getHash*(fileIdx: int32): string =
assert fileIdx >= 0
shallowCopy(result, fileInfos[fileIdx].hash)
proc getHash*(fileIdx: FileIndex): string =
assert fileIdx.int32 >= 0
shallowCopy(result, fileInfos[fileIdx.int32].hash)
proc toFullPathConsiderDirty*(fileIdx: int32): string =
if fileIdx < 0:
proc toFullPathConsiderDirty*(fileIdx: FileIndex): string =
if fileIdx.int32 < 0:
result = "???"
elif not fileInfos[fileIdx].dirtyFile.isNil:
result = fileInfos[fileIdx].dirtyFile
elif not fileInfos[fileIdx.int32].dirtyFile.isNil:
result = fileInfos[fileIdx.int32].dirtyFile
else:
result = fileInfos[fileIdx].fullPath
result = fileInfos[fileIdx.int32].fullPath
template toFilename*(info: TLineInfo): string =
info.fileIndex.toFilename
@@ -746,15 +763,15 @@ template toFullPath*(info: TLineInfo): string =
info.fileIndex.toFullPath
proc toMsgFilename*(info: TLineInfo): string =
if info.fileIndex < 0:
if info.fileIndex.int32 < 0:
result = "???"
elif gListFullPaths:
result = fileInfos[info.fileIndex].fullPath
result = fileInfos[info.fileIndex.int32].fullPath
else:
result = fileInfos[info.fileIndex].projPath
result = fileInfos[info.fileIndex.int32].projPath
proc toLinenumber*(info: TLineInfo): int {.inline.} =
result = info.line
result = int info.line
proc toColumn*(info: TLineInfo): int {.inline.} =
result = info.col
@@ -771,7 +788,7 @@ proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes
result = filename in info.toFilename
const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions
const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
# are produced within comments and string literals
var gTrackPos*: TLineInfo
var gTrackPosAttached*: bool ## whether the tracking position was attached to some
@@ -910,7 +927,7 @@ proc writeContext(lastinfo: TLineInfo) =
else:
styledMsgWriteln(styleBright,
PosFormat % [toMsgFilename(msgContext[i]),
coordToStr(msgContext[i].line),
coordToStr(msgContext[i].line.int),
coordToStr(msgContext[i].col+1)],
resetStyle,
getMessageStr(errInstantiationFrom, ""))
@@ -978,7 +995,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
of warnMin..warnMax: WarningTitle
of hintMin..hintMax: HintTitle
else: ErrorTitle
result = PosFormat % [toMsgFilename(info), coordToStr(info.line),
result = PosFormat % [toMsgFilename(info), coordToStr(info.line.int),
coordToStr(info.col+1)] &
title &
getMessageStr(msg, arg)
@@ -1019,7 +1036,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
# NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here
let x = PosFormat % [toMsgFilename(info), coordToStr(info.line),
let x = PosFormat % [toMsgFilename(info), coordToStr(info.line.int),
coordToStr(info.col+1)]
let s = getMessageStr(msg, arg)
@@ -1032,7 +1049,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
KindColor, `%`(KindFormat, kind))
else:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s)
if msg in errMin..errMax and hintSource in gNotes:
if hintSource in gNotes:
info.writeSurroundingSrc
handleError(msg, eh, s)
@@ -1077,30 +1094,30 @@ template assertNotNil*(e): untyped =
template internalAssert*(e: bool) =
if not e: internalError($instantiationInfo())
proc addSourceLine*(fileIdx: int32, line: string) =
fileInfos[fileIdx].lines.add line.rope
proc addSourceLine*(fileIdx: FileIndex, line: string) =
fileInfos[fileIdx.int32].lines.add line.rope
proc sourceLine*(i: TLineInfo): Rope =
if i.fileIndex < 0: return nil
if i.fileIndex.int32 < 0: return nil
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
if not optPreserveOrigSource and fileInfos[i.fileIndex.int32].lines.len == 0:
try:
for line in lines(i.toFullPath):
addSourceLine i.fileIndex, line.string
except IOError:
discard
internalAssert i.fileIndex < fileInfos.len
internalAssert i.fileIndex.int32 < fileInfos.len
# can happen if the error points to EOF:
if i.line > fileInfos[i.fileIndex].lines.len: return nil
if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
result = fileInfos[i.fileIndex].lines[i.line-1]
result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
proc quotedFilename*(i: TLineInfo): Rope =
internalAssert i.fileIndex >= 0
internalAssert i.fileIndex.int32 >= 0
if optExcessiveStackTrace in gGlobalOptions:
result = fileInfos[i.fileIndex].quotedFullName
result = fileInfos[i.fileIndex.int32].quotedFullName
else:
result = fileInfos[i.fileIndex].quotedName
result = fileInfos[i.fileIndex.int32].quotedName
ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
case err

View File

@@ -90,14 +90,6 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
elif gCmd == cmdCompileToPHP:
var ex: string
if options.outFile.len > 0:
ex = options.outFile.prependCurDir.quoteShell
else:
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir))
execExternalProgram("php " & ex & ' ' & commands.arguments)
else:
var binPath: string
if options.outFile.len > 0:

View File

@@ -28,7 +28,7 @@ proc overwriteFiles*() =
let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
for i in 0 .. high(gSourceFiles):
if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx):
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex):
let newFile = if gOverWrite: gSourceFiles[i].fullpath
else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
try:
@@ -95,7 +95,7 @@ proc beautifyName(s: string, k: TSymKind): string =
proc replaceInFile(info: TLineInfo; newName: string) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
@@ -107,8 +107,8 @@ proc replaceInFile(info: TLineInfo; newName: string) =
if differ(line, first, last, newName):
# last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int].dirty = true
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k)
@@ -136,7 +136,7 @@ template styleCheckDef*(s: PSym) =
styleCheckDef(s.info, s, s.kind)
proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
if info.fileIndex < 0: return
if info.fileIndex.int < 0: return
# we simply convert it to what it looks like in the definition
# for consistency

View File

@@ -16,13 +16,13 @@ type
lines*: seq[string]
dirty*, isNimfixFile*: bool
fullpath*, newline*: string
fileIdx*: int32
fileIdx*: FileIndex
var
gSourceFiles*: seq[TSourceFile] = @[]
proc loadFile*(info: TLineInfo) =
let i = info.fileIndex
let i = info.fileIndex.int
if i >= gSourceFiles.len:
gSourceFiles.setLen(i+1)
if gSourceFiles[i].lines.isNil:
@@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool =
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
@@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int32].dirty = true
#if newSym.s == "File": writeStackTrace()
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
@@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
proc replaceComment*(info: TLineInfo) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = info.col.int
if line[first] != '#': inc first
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int32].dirty = true

View File

@@ -81,7 +81,6 @@ type
# **keep binary compatible**
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS,
cmdCompileToPHP,
cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
cmdGenDepend, cmdDump,
cmdCheck, # semantic checking for whole project

View File

@@ -235,7 +235,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arLValue
else:
result = isAssignable(owner, n.sons[0], isUnsafeAddr)
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
if result != arNone and n[1].kind == nkSym and
sfDiscriminant in n[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in

View File

@@ -83,7 +83,7 @@ proc getTok(p: var TParser) =
rawGetTok(p.lex, p.tok)
p.hasProgress = true
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
cache: IdentCache;
strongSpaces=false) =
## Open a parser, using the given arguments to set up its internal state.
@@ -125,7 +125,13 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment:
if node != nil:
if node.comment == nil: node.comment = ""
add(node.comment, p.tok.literal)
when defined(nimpretty):
if p.tok.commentOffsetB > p.tok.commentOffsetA:
add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
else:
add node.comment, p.tok.literal
else:
add(node.comment, p.tok.literal)
else:
parMessage(p, errInternal, "skipComment")
getTok(p)

View File

@@ -52,8 +52,8 @@ proc makePass*(open: TPassOpen = nil,
# the semantic checker needs these:
var
gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.}
gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.}
gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.}
gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.}
# implementation
@@ -63,20 +63,6 @@ proc skipCodegen*(n: PNode): bool {.inline.} =
# error count instead.
result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
if (s.kind in {skMethod, skProc, skFunc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false
# XXX this doesn't really make sense with excessive CTFE
else:
result = true
const
maxPasses = 10
@@ -153,7 +139,7 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
m = gPasses[i].close(graph, a[i], m)
a[i] = nil # free the memory here
proc resolveMod(module, relativeTo: string): int32 =
proc resolveMod(module, relativeTo: string): FileIndex =
let fullPath = findModule(module, relativeTo)
if fullPath.len == 0:
result = InvalidFileIDX
@@ -166,7 +152,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
let relativeTo = m.info.toFullPath
for module in items(implicits):
# implicit imports should not lead to a module importing itself
if m.position != resolveMod(module, relativeTo):
if m.position != resolveMod(module, relativeTo).int32:
var importStmt = newNodeI(nodeKind, gCmdLineInfo)
var str = newStrNode(nkStrLit, module)
str.info = gCmdLineInfo
@@ -180,7 +166,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
p: TParsers
a: TPassContextArray
s: PLLStream
fileIdx = module.fileIdx
fileIdx = FileIndex module.fileIdx
if module.id < 0:
# new module caching mechanism:
for i in 0..<gPassesLen:

View File

@@ -19,7 +19,7 @@ proc getTok(p: var TParser) =
## `tok` member.
rawGetTok(p.lex, p.tok)
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream;
proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream;
cache: IdentCache) =
## Open a parser, using the given arguments to set up its internal state.
##

View File

@@ -543,7 +543,7 @@ proc pragmaLine(c: PContext, n: PNode) =
else:
# XXX this produces weird paths which are not properly resolved:
n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
n.info.line = int16(y.intVal)
n.info.line = uint16(y.intVal)
else:
localError(n.info, errXExpected, "tuple")
else:

View File

@@ -66,12 +66,10 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
const flags = {ExactGenericParams, ExactTypeDescValues,
ExactConstraints, IgnoreCC}
var it: TIdentIter
result = initIdentIter(it, scope.symbols, fn.name)
while result != nil:
if result.kind == fn.kind and sameType(result.typ, fn.typ, flags):
if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
case equalParams(result.typ.n, fn.typ.n)
of paramsEqual:
if (sfExported notin result.flags) and (sfExported in fn.flags):
@@ -85,11 +83,8 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
return
of paramsNotEqual:
discard
result = nextIdentIter(it, scope.symbols)
return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
result = searchForProcNew(c, scope, fn)
when false:

View File

@@ -39,8 +39,7 @@ type
inPragma: int
when defined(nimpretty):
pendingNewlineCount: int
origContent: string
fid*: FileIndex
# We render the source code in a two phases: The first
# determines how long the subtree will likely be, the second
@@ -354,13 +353,13 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc atom(g: TSrcGen; n: PNode): string =
when defined(nimpretty):
let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
" " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB)
" " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
else:
""
if n.info.offsetA <= n.info.offsetB:
# for some constructed tokens this can not be the case and we're better
# off to not mess with the offset then.
return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment
return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
var f: float32
case n.kind
of nkEmpty: result = ""
@@ -1460,17 +1459,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
proc `$`*(n: PNode): string = n.renderTree
proc renderModule*(n: PNode, infile, outfile: string,
renderFlags: TRenderFlags = {}) =
renderFlags: TRenderFlags = {};
fid = FileIndex(-1)) =
var
f: File
g: TSrcGen
initSrcGen(g, renderFlags)
when defined(nimpretty):
try:
g.origContent = readFile(infile)
except IOError:
rawMessage(errCannotOpenFile, infile)
g.fid = fid
for i in countup(0, sonsLen(n) - 1):
gsub(g, n.sons[i])
optNL(g)

View File

@@ -1,6 +1,6 @@
import
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
import
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables
type
@@ -135,13 +135,13 @@ proc hasIncludes(n:PNode): bool =
if a.kind == nkIncludeStmt:
return true
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache)
graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx)
graph.addIncludeDep(FileIndex s.position, fileIdx)
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
modulePath: string, includedFiles: var IntSet,
cache: IdentCache): PNode =
# Parses includes and injects them in the current tree
@@ -153,13 +153,13 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
for i in 0..<a.len:
var f = checkModuleName(a.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(includedFiles, f):
if containsOrIncl(includedFiles, f.int):
localError(a.info, errRecursiveDependencyX, f.toFilename)
else:
let nn = includeModule(graph, module, f, cache)
let nnn = expandIncludes(graph, module, nn, modulePath,
let nnn = expandIncludes(graph, module, nn, modulePath,
includedFiles, cache)
excl(includedFiles, f)
excl(includedFiles, f.int)
for b in nnn:
result.add b
else:
@@ -429,8 +429,8 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
if n.hasForbiddenPragma:
return n
var includedFiles = initIntSet()
let mpath = module.fileIdx.toFullPath
let n = expandIncludes(graph, module, n, mpath,
let mpath = module.fileIdx.FileIndex.toFullPath
let n = expandIncludes(graph, module, n, mpath,
includedFiles, cache).splitSections
result = newNodeI(nkStmtList, n.info)
var deps = newSeq[(IntSet, IntSet)](n.len)

View File

@@ -9,124 +9,18 @@
## This module implements the canonalization for the various caching mechanisms.
import ast, idgen
import ast, idgen, msgs
when not defined(nimSymbolfiles):
template setupModuleCache* = discard
template storeNode*(module: PSym; n: PNode) = discard
template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
template getModuleId*(fileIdx: int32; fullpath: string): int = getID()
template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
template addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = discard
template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
template storeRemaining*(module: PSym) = discard
else:
include rodimpl
when false:
type
BlobWriter* = object
buf: string
pos: int
SerializationAction = enum acRead, acWrite
# Varint implementation inspired by SQLite.
proc rdVaruint64(z: ptr UncheckedArray[byte]; n: int; pResult: var uint64): int =
if z[0] <= 240:
pResult = z[0]
return 1
if z[0] <= 248:
if n < 2: return 0
pResult = (z[0] - 241) * 256 + z[1] + 240
return 2
if n < z[0]-246: return 0
if z[0] == 249:
pResult = 2288 + 256*z[1] + z[2]
return 3
if z[0] == 250:
pResult = (z[1] shl 16u64) + (z[2] shl 8u64) + z[3]
return 4
let x = (z[1] shl 24) + (z[2] shl 16) + (z[3] shl 8) + z[4]
if z[0] == 251:
pResult = x
return 5
if z[0] == 252:
pResult = (((uint64)x) shl 8) + z[5]
return 6
if z[0] == 253:
pResult = (((uint64)x) shl 16) + (z[5] shl 8) + z[6]
return 7
if z[0] == 254:
pResult = (((uint64)x) shl 24) + (z[5] shl 16) + (z[6] shl 8) + z[7]
return 8
pResult = (((uint64)x) shl 32) +
(0xffffffff & ((z[5] shl 24) + (z[6] shl 16) + (z[7] shl 8) + z[8]))
return 9
proc varintWrite32(z: ptr UncheckedArray[byte]; y: uint32) =
z[0] = uint8(y shr 24)
z[1] = uint8(y shr 16)
z[2] = uint8(y shr 8)
z[3] = uint8(y)
proc sqlite4PutVarint64(z: ptr UncheckedArray[byte], x: uint64): int =
## Write a varint into z. The buffer z must be at least 9 characters
## long to accommodate the largest possible varint. Returns the number of
## bytes used.
if x <= 240:
z[0] = uint8 x
return 1
if x <= 2287:
y = uint32(x - 240)
z[0] = uint8(y shr 8 + 241)
z[1] = uint8(y and 255)
return 2
if x <= 67823:
y = uint32(x - 2288)
z[0] = 249
z[1] = uint8(y shr 8)
z[2] = uint8(y and 255)
return 3
let y = uint32 x
let w = uint32(x shr 32)
if w == 0:
if y <= 16777215:
z[0] = 250
z[1] = uint8(y shr 16)
z[2] = uint8(y shr 8)
z[3] = uint8(y)
return 4
z[0] = 251
varintWrite32(z+1, y)
return 5
if w <= 255:
z[0] = 252
z[1] = uint8 w
varintWrite32(z+2, y)
return 6
if w <= 65535:
z[0] = 253
z[1] = uint8(w shr 8)
z[2] = uint8 w
varintWrite32(z+3, y)
return 7
if w <= 16777215:
z[0] = 254
z[1] = uint8(w shr 16)
z[2] = uint8(w shr 8)
z[3] = uint8 w
varintWrite32(z+4, y)
return 8
z[0] = 255
varintWrite32(z+1, w)
varintWrite32(z+5, y)
return 9
template field(x: BiggestInt; action: SerializationAction) =
when action == acRead:
readBiggestInt(x)
else:
writeBiggestInt()

View File

@@ -126,8 +126,8 @@ type
s: cstring # mmap'ed file contents
options: TOptions
reason: TReasonForRecompile
modDeps: seq[int32]
files: seq[int32]
modDeps: seq[FileIndex]
files: seq[FileIndex]
dataIdx: int # offset of start of data section
convertersIdx: int # offset of start of converters section
initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
@@ -163,11 +163,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
else: info.col = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',':
inc(r.pos)
if r.s[r.pos] == ',': info.line = -1'i16
else: info.line = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': info.line = 0'u16
else: info.line = uint16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',':
inc(r.pos)
info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
proc skipNode(r: PRodReader) =
assert r.s[r.pos] == '('
@@ -586,7 +586,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
# new command forces us to consider it here :-)
case old
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM:
cmdCompileToJS, cmdCompileToLLVM:
if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
cmdInteractive}:
return false
@@ -861,29 +861,29 @@ proc loadMethods(r: PRodReader) =
r.methods.add(rrGetSym(r, d, unknownLineInfo()))
if r.s[r.pos] == ' ': inc(r.pos)
proc getHash*(fileIdx: int32): SecureHash =
if fileIdx <% gMods.len and gMods[fileIdx].hashDone:
return gMods[fileIdx].hash
proc getHash*(fileIdx: FileIndex): SecureHash =
if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone:
return gMods[fileIdx.int32].hash
result = secureHashFile(fileIdx.toFullPath)
if fileIdx >= gMods.len: setLen(gMods, fileIdx+1)
gMods[fileIdx].hash = result
if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1)
gMods[fileIdx.int32].hash = result
template growCache*(cache, pos) =
if cache.len <= pos: cache.setLen(pos+1)
proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
proc checkDep(fileIdx: FileIndex; cache: IdentCache): TReasonForRecompile =
assert fileIdx != InvalidFileIDX
growCache gMods, fileIdx
if gMods[fileIdx].reason != rrEmpty:
growCache gMods, fileIdx.int32
if gMods[fileIdx.int32].reason != rrEmpty:
# reason has already been computed for this module:
return gMods[fileIdx].reason
return gMods[fileIdx.int32].reason
let filename = fileIdx.toFilename
var hash = getHash(fileIdx)
gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles
gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles
result = rrNone
var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
var r = newRodReader(rodfile, hash, fileIdx, cache)
var r = newRodReader(rodfile, hash, fileIdx.int32, cache)
if r == nil:
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
else:
@@ -907,19 +907,18 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
# recompilation is necessary:
if r != nil: memfiles.close(r.memfile)
r = nil
gMods[fileIdx].rd = r
gMods[fileIdx].reason = result # now we know better
gMods[fileIdx.int32].rd = r
gMods[fileIdx.int32].reason = result # now we know better
proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
let fileIdx = module.fileIdx
if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}:
module.id = getID()
return nil
idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
let fileIdx = FileIndex module.fileIdx
discard checkDep(fileIdx, cache)
if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
result = gMods[fileIdx].rd
if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile")
result = gMods[fileIdx.int32].rd
if result != nil:
module.id = result.moduleID
result.syms[module.id] = module
@@ -1153,7 +1152,7 @@ proc viewFile(rodfile: string) =
if r.s[r.pos] == '\x0A':
inc(r.pos)
inc(r.line)
outf.write(w, " ", inclHash, "\n")
outf.write(w.int32, " ", inclHash, "\n")
if r.s[r.pos] == ')': inc(r.pos)
outf.write(")\n")
of "DEPS":
@@ -1163,7 +1162,7 @@ proc viewFile(rodfile: string) =
let v = int32(decodeVInt(r.s, r.pos))
r.modDeps.add(r.files[v])
if r.s[r.pos] == ' ': inc(r.pos)
outf.write(" ", r.files[v])
outf.write(" ", r.files[v].int32)
outf.write("\n")
of "INTERF", "COMPILERPROCS":
inc r.pos, 2

View File

@@ -55,7 +55,7 @@ proc fileIdx(w: PRodWriter, filename: string): int =
w.files[result] = filename
template filename*(w: PRodWriter): string =
w.module.filename
toFilename(FileIndex w.module.position)
proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter =
new(result)
@@ -125,14 +125,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
encodeVInt(int n.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFullPath(n.info)), result)
elif fInfo.line != n.info.line:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
encodeVInt(int n.info.line, result)
elif fInfo.col != n.info.col:
result.add('?')
encodeVInt(n.info.col, result)
@@ -303,7 +303,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
result.add('?')
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
if s.info.line != 0'u16: encodeVInt(int s.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFullPath(s.info)), result)
if s.owner != nil:
@@ -642,7 +642,7 @@ proc process(c: PPassContext, n: PNode): PNode =
proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
if module.id < 0: internalError("rodwrite: module ID not set")
var w = newRodWriter(rodread.getHash module.fileIdx, module, cache)
var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache)
rawAddInterfaceSym(w, module)
result = w

View File

@@ -377,7 +377,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
## reassigned, and binding the unbound identifiers that the macro output
## contains.
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
if evalTemplateCounter > evalTemplateLimit:
globalError(s.info, errTemplateInstantiationTooNested)
c.friendModules.add(s.owner.getModule)

View File

@@ -144,7 +144,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
proc filename*(c: PContext): string =
# the module's filename
return c.module.filename
return toFilename(FileIndex c.module.position)
proc scopeDepth*(c: PContext): int {.inline.} =
result = if c.currentScope != nil: c.currentScope.depthLevel

View File

@@ -526,6 +526,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
if isAssignable(c, it) notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
localError(it.info, errVarForOutParamNeededX, $it)
# bug #5113: disallow newSeq(result) where result is a 'var T':
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
var arg = n[1] #.skipAddr
if arg.kind == nkHiddenDeref: arg = arg[0]
if arg.kind == nkSym and arg.sym.kind == skResult and
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
localError(n.info, errXStackEscape, renderTree(n[1], {renderNoComments}))
return
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkHiddenCallConv:
@@ -1334,11 +1342,11 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
let root = exprRoot(n)
if root != nil and root.owner == c.p.owner:
if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags:
localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [
root.name.s, renderTree(n, {renderNoComments})])
localError(n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [
root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
elif root.kind == skParam and root.position != 0:
localError(n.info, "'$1' is not the first parameter; context: '$2'" % [
root.name.s, renderTree(n, {renderNoComments})])
localError(n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [
root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl])
case n.kind
of nkHiddenAddr, nkAddr: return n
of nkHiddenDeref, nkDerefExpr: return n.sons[0]

View File

@@ -483,6 +483,19 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
proc getConstExpr(m: PSym, n: PNode): PNode =
result = nil
proc getSrcTimestamp(): DateTime =
try:
result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
"not a number"))))
except ValueError:
# Environment variable malformed.
# https://reproducible-builds.org/specs/source-date-epoch/: "If the
# value is malformed, the build process SHOULD exit with a non-zero
# error code", which this doesn't do. This uses local time, because
# that maintains compatibility with existing usage.
result = local(getTime())
case n.kind
of nkSym:
var s = n.sym
@@ -492,8 +505,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of skConst:
case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
of mCompileTime: result = newStrNodeT(times.getClockStr(), n)
of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(),
"yyyy-MM-dd"), n)
of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
"HH:mm:ss"), n)
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)

View File

@@ -779,20 +779,15 @@ proc semRaise(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if n[0].kind != nkEmpty:
n[0] = semExprWithType(c, n[0])
let typ = n[0].typ
if not isImportedException(typ):
if typ.kind != tyRef or typ.lastSon.kind != tyObject:
localError(n.info, errExprCannotBeRaised)
if not isException(typ.lastSon):
localError(n.info, "raised object of type $1 does not inherit from Exception",
[typeToString(typ)])
proc addGenericParamListToScope(c: PContext, n: PNode) =
if n.kind != nkGenericParams: illFormedAst(n)
for i in countup(0, sonsLen(n)-1):
@@ -1100,12 +1095,12 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
for i in 0..<n.len:
var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f):
if containsOrIncl(c.includedFiles, f.int):
localError(n.info, errRecursiveDependencyX, f.toFilename)
else:
let code = gIncludeFile(c.graph, c.module, f, c.cache)
gatherStmts c, code, result
excl(c.includedFiles, f)
excl(c.includedFiles, f.int)
of nkStmtList:
for i in 0 ..< n.len:
gatherStmts(c, n.sons[i], result)
@@ -1789,11 +1784,11 @@ proc evalInclude(c: PContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(n.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f):
if containsOrIncl(c.includedFiles, f.int):
localError(n.info, errRecursiveDependencyX, f.toFilename)
else:
addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache)))
excl(c.includedFiles, f)
excl(c.includedFiles, f.int)
proc setLine(n: PNode, info: TLineInfo) =
for i in 0 ..< safeLen(n): setLine(n.sons[i], info)

View File

@@ -415,7 +415,7 @@ when defined(nimsuggest):
# Since TLineInfo defined a == operator that doesn't include the column,
# we map TLineInfo to a unique int here for this lookup table:
proc infoToInt(info: TLineInfo): int64 =
info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48
info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48
proc addNoDup(s: PSym; info: TLineInfo) =
# ensure nothing gets too slow:
@@ -574,7 +574,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
if outputs.len > 0: suggestQuit()
proc suggestSentinel*(c: PContext) =
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return
if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return
if c.compilesContextId > 0: return
inc(c.compilesContextId)
var outputs: Suggestions = @[]

View File

@@ -138,7 +138,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
else:
result = applyFilter(p, n, filename, result)
proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream;
proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache) =
var s: PLLStream
p.skin = skinStandard
@@ -155,7 +155,7 @@ proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream;
proc closeParsers*(p: var TParsers) =
parser.closeParser(p.parser)
proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} =
proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} =
var
p: TParsers
f: File

View File

@@ -777,8 +777,8 @@ proc equalParams(a, b: PNode): TParamsEquality =
return paramsNotEqual # paramsIncompatible;
# continue traversal! If not equal, we can return immediately; else
# it stays incompatible
if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}):
if (a.sons[0].typ == nil) or (b.sons[0].typ == nil):
if not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}):
if (a.typ == nil) or (b.typ == nil):
result = paramsNotEqual # one proc has a result, the other not is OK
else:
result = paramsIncompatible # overloading by different
@@ -970,7 +970,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameFlags(a, b)
of tyGenericParam:
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and ExactGenericParams in c.flags:
if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
result = a.sym.position == b.sym.position
of tyGenericInvocation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink,

View File

@@ -1381,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNGetLine:
decodeB(rkNode)
let n = regs[rb].node
regs[ra].node = newIntNode(nkIntLit, n.info.line)
regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
of opcNGetColumn:
@@ -1397,8 +1397,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let bNode = regs[rc].node
# these are cstring to prevent string copy, and cmpIgnoreStyle from
# takes cstring arguments
var aStrVal: cstring
var bStrVal: cstring
var aStrVal: cstring = nil
var bStrVal: cstring = nil
# extract strVal from argument ``a``
case aNode.kind
of {nkStrLit..nkTripleStrLit}:
@@ -1407,8 +1407,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
aStrVal = aNode.ident.s.cstring
of nkSym:
aStrVal = aNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
aStrVal = aNode[0].sym.name.s.cstring
else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
discard
# extract strVal from argument ``b``
case bNode.kind
of {nkStrLit..nkTripleStrLit}:
@@ -1417,11 +1419,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
bStrVal = bNode.ident.s.cstring
of nkSym:
bStrVal = bNode.sym.name.s.cstring
of nkOpenSymChoice, nkClosedSymChoice:
bStrVal = bNode[0].sym.name.s.cstring
else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
discard
# set result
regs[ra].intVal =
ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
if aStrVal != nil and bStrVal != nil:
ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0)
else:
0
of opcStrToIdent:
decodeB(rkNode)
if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}:
@@ -1513,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let x = newNodeI(TNodeKind(int(k)),
if cc.kind != nkNilLit:
cc.info
elif c.comesFromHeuristic.line > -1:
elif c.comesFromHeuristic.line != 0'u16:
c.comesFromHeuristic
elif c.callsite != nil and c.callsite.safeLen > 1:
c.callsite[1].info
@@ -1685,7 +1693,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode,
newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if result.info.col < 0: result.info = n.info
proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode =
result = evalConstExprAux(module, cache, nil, e, emConst)
@@ -1721,14 +1729,16 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
let posInCall = macroSym.typ.len + i
yield (genericParam, call[posInCall])
# to prevent endless recursion in macro instantiation
const evalMacroLimit = 1000
var evalMacroCounter: int
proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
sym: PSym): PNode =
# XXX globalError() is ugly here, but I don't know a better solution for now
inc(evalMacroCounter)
if evalMacroCounter > 100:
globalError(n.info, errTemplateInstantiationTooNested)
if evalMacroCounter > evalMacroLimit:
globalError(n.info, errMacroInstantiationTooNested)
# immediate macros can bypass any type and arity checking so we check the
# arity here too:
@@ -1738,7 +1748,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
setupGlobalCtx(module, cache)
var c = globalCtx
c.comesFromHeuristic.line = -1
c.comesFromHeuristic.line = 0'u16
c.callsite = nOrig
let start = genProc(c, sym)

View File

@@ -76,7 +76,7 @@ Advanced options:
--NimblePath:PATH add a path for Nimble support
--noNimblePath deactivate the Nimble path
--noCppExceptions use default exception handling with C++ backend
--cppCompileToNamespace use namespace "Nim" for the generated C++ code
--cppCompileToNamespace use namespace "Nim" for the generated C++ code
--excludePath:PATH exclude a path from the list of search paths
--dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL
has no effect and can be statically linked instead;

View File

@@ -36,7 +36,7 @@ Options:
--app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library
-r, --run run the compiled program with given arguments
--advanced show advanced command line switches
--fullhelp show all command line switches
-h, --help show this help
Note, single letter options that take an argument require a colon. E.g. -p:PATH.

View File

@@ -3487,17 +3487,17 @@ returned value is an l-value and can be modified by the caller:
.. code-block:: nim
var g = 0
proc WriteAccessToG(): var int =
proc writeAccessToG(): var int =
result = g
WriteAccessToG() = 6
writeAccessToG() = 6
assert g == 6
It is a compile time error if the implicitly introduced pointer could be
used to access a location beyond its lifetime:
.. code-block:: nim
proc WriteAccessToG(): var int =
proc writeAccessToG(): var int =
var g = 0
result = g # Error!
@@ -3512,6 +3512,24 @@ In the standard library every name of a routine that returns a ``var`` type
starts with the prefix ``m`` per convention.
.. include:: manual/var_t_return.rst
Future directions
~~~~~~~~~~~~~~~~~
Later versions of Nim can be more precise about the borrowing rule with
a syntax like:
.. code-block:: nim
proc foo(other: Y; container: var X): var T from container
Here ``var T from container`` explicitly exposes that the
location is deviated from the second parameter (called
'container' in this case). The syntax ``var T from p`` specifies a type
``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``.
Overloading of the subscript operator
-------------------------------------
@@ -5241,15 +5259,21 @@ chance to convert it into a sequence.
Macros
======
A macro is a special kind of low level template. Macros can be used
to implement `domain specific languages`:idx:.
A macro is a special function that is executed at compile-time.
Normally the input for a macro is an abstract syntax
tree (AST) of the code that is passed to it. The macro can then do
transformations on it and return the transformed AST. The
transformed AST is then passed to the compiler as if the macro
invocation would have been replaced by its result in the source
code. This can be used to implement `domain specific
languages`:idx:.
While macros enable advanced compile-time code transformations, they
cannot change Nim's syntax. However, this is no real restriction because
Nim's syntax is flexible enough anyway.
To write macros, one needs to know how the Nim concrete syntax is converted
to an abstract syntax tree.
to an AST.
There are two ways to invoke a macro:
(1) invoking a macro like a procedure call (`expression macros`)
@@ -5269,19 +5293,21 @@ variable number of arguments:
# ``macros`` module:
import macros
macro debug(n: varargs[untyped]): untyped =
# `n` is a Nim AST that contains the whole macro invocation
# this macro returns a list of statements:
result = newNimNode(nnkStmtList, n)
macro debug(args: varargs[untyped]): untyped =
# `args` is a collection of `NimNode` values that each contain the
# AST for an argument of the macro. A macro always has to
# return a `NimNode`. A node of kind `nnkStmtList` is suitable for
# this use case.
result = nnkStmtList.newTree()
# iterate over any argument that is passed to this macro:
for i in 0..n.len-1:
for n in args:
# add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation:
add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
# add a call to the statement list that writes ": "
add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
result.add newCall("write", newIdentNode("stdout"), newLit(": "))
# add a call to the statement list that writes the expressions value:
add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
result.add newCall("writeLine", newIdentNode("stdout"), n)
var
a: array[0..10, int]
@@ -8187,5 +8213,3 @@ validation errors:
If the taint mode is turned off, ``TaintedString`` is simply an alias for
``string``.

View File

@@ -0,0 +1,20 @@
Memory safety for returning by ``var T`` is ensured by a simple borrowing
rule: If ``result`` does not refer to a location pointing to the heap
(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access)
then it has to be deviated by the routine's first parameter:
.. code-block:: nim
proc forward[T](x: var T): var T =
result = x # ok, deviated from the first parameter.
proc p(param: var int): var int =
var x: int
# we know 'forward' provides a view into the location deviated by
# its first argument 'x'.
result = forward(x) # Error: location is derived from ``x``
# which is not p's first parameter and lives
# on the stack.
In other words, the lifetime of what ``result`` points to is attached to the
lifetime of the first parameter and that is enough knowledge to verify
memory safety at the callsite.

View File

@@ -706,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
res.add(($n.kind).substr(3))
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: res.add(" nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal)
of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym:
@@ -730,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
add(result, "(")
case n.kind
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil")
of nnkEmpty, nnkNilLit: discard # same as nil node in this representation
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym:
@@ -766,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
## See also `repr`, `treeRepr`, and `lispRepr`.
const
NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
@@ -775,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
res.add("new" & ($n.kind).substr(3) & "Node(")
elif n.kind in LitKinds:
res.add("newLit(")
elif n.kind == nnkNilLit:
res.add("newNilLit()")
else:
res.add($n.kind)
case n.kind
of nnkEmpty: discard
of nnkNilLit: res.add("nil")
of nnkEmpty, nnkNilLit: discard
of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
@@ -1289,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode =
typ = n.getTypeInst()
if typ.typeKind == ntyTypeDesc:
return typ[1].getImpl()[0][1]
let impl = typ[1].getImpl()
if impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
return impl[0] # handle types which don't have macro at all
if n.kind == nnkSym: # either an variable or a proc
let impl = n.getImpl()

View File

@@ -101,8 +101,8 @@ when defined(windows):
from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select
else:
from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet,
FD_ISSET, select
from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO,
TFdSet, FD_ISSET, select
type
DelegateObj* = object
@@ -556,8 +556,12 @@ proc send*(sock: AsyncSocket, data: string) =
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) =
FD_ZERO(fd)

View File

@@ -953,8 +953,12 @@ when defined(ssl):
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when defined(posix):
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
else:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) =
FD_ZERO(fd)

View File

@@ -351,8 +351,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec*: clong ## Seconds.
tv_usec*: clong ## Microseconds.
tv_sec*: Time ## Seconds.
tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object
abi: array[1024 div (8 * sizeof(clong)), clong]

View File

@@ -335,8 +335,8 @@ type
Timeval* {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec*: int ## Seconds.
tv_usec*: int ## Microseconds.
tv_sec*: Time ## Seconds.
tv_usec*: Suseconds ## Microseconds.
TFdSet* {.importc: "fd_set", header: "<sys/select.h>",
final, pure.} = object
Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>",

View File

@@ -168,6 +168,15 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) =
signal(w.q.empty)
signal(w.taskArrived)
proc attach(fv: FlowVarBase; i: int): bool =
acquire(fv.cv.L)
if fv.cv.counter <= 0:
fv.idx = i
result = true
else:
result = false
release(fv.cv.L)
proc finished(fv: FlowVarBase) =
doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
# we have to protect against the rare cases where the owner of the flowVar
@@ -245,26 +254,27 @@ proc `^`*[T](fv: FlowVar[T]): T =
proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
## awaits any of the given flowVars. Returns the index of one flowVar for
## which a value arrived. A flowVar only supports one call to 'awaitAny' at
## the same time. That means if you await([a,b]) and await([b,c]) the second
## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
## call will only await 'c'. If there is no flowVar left to be able to wait
## on, -1 is returned.
## **Note**: This results in non-deterministic behaviour and so should be
## avoided.
## **Note**: This results in non-deterministic behaviour and should be avoided.
var ai: AwaitInfo
ai.cv.initSemaphore()
var conflicts = 0
result = -1
for i in 0 .. flowVars.high:
if cas(addr flowVars[i].ai, nil, addr ai):
flowVars[i].idx = i
if not attach(flowVars[i], i):
result = i
break
else:
inc conflicts
if conflicts < flowVars.len:
await(ai.cv)
result = ai.idx
if result < 0:
await(ai.cv)
result = ai.idx
for i in 0 .. flowVars.high:
discard cas(addr flowVars[i].ai, addr ai, nil)
else:
result = -1
destroySemaphore(ai.cv)
proc isReady*(fv: FlowVarBase): bool =

View File

@@ -616,8 +616,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) =
proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
when useWinVersion:
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
else:
result.tv_sec = seconds.Time
result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd)

View File

@@ -104,6 +104,10 @@ proc none*(T: typedesc): Option[T] =
# the default is the none type
discard
proc none*[T]: Option[T] =
## Alias for ``none(T)``.
none(T)
proc isSome*[T](self: Option[T]): bool {.inline.} =
when T is SomePointer:
self.val != nil
@@ -290,3 +294,7 @@ when isMainModule:
let tmp = option(intref)
check(sizeof(tmp) == sizeof(ptr int))
test "none[T]":
check(none[int]().isNone)
check(none(int) == none[int]())

View File

@@ -188,7 +188,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64)
result = fromWinTime(rdFileTime(f.ftLastWriteTime))
findClose(h)
proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@@ -201,7 +201,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64)
result = fromWinTime(rdFileTime(f.ftLastAccessTime))
findClose(h)
proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
@@ -218,7 +218,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
var f: WIN32_FIND_DATA
var h = findFirstFile(file, f)
if h == -1'i32: raiseOSError(osLastError())
result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64)
result = fromWinTime(rdFileTime(f.ftCreationTime))
findClose(h)
proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
@@ -329,20 +329,21 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
c_free(cast[pointer](r))
when defined(Windows):
proc openHandle(path: string, followSymlink=true): Handle =
proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
if not followSymlink:
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
let access = if writeAccess: GENERIC_WRITE else: 0'i32
when useWinUnicode:
result = createFileW(
newWideCString(path), 0'i32,
newWideCString(path), access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0
)
else:
result = createFileA(
path, 0'i32,
path, access,
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, flags, 0
)
@@ -1511,16 +1512,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'Stat' structure on posix
when defined(Windows):
template toTime(e: FILETIME): untyped {.gensym.} =
fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics
template merge(a, b): untyped = a or (b shl 32)
formalInfo.id.device = rawInfo.dwVolumeSerialNumber
formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
formalInfo.linkCount = rawInfo.nNumberOfLinks
formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime)
formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime)
formalInfo.creationTime = toTime(rawInfo.ftCreationTime)
formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime))
# Retrieve basic permissions
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
@@ -1654,3 +1653,18 @@ proc isHidden*(path: string): bool =
result = (fileName[0] == '.') and (fileName[3] != '.')
{.pop.}
proc setLastModificationTime*(file: string, t: times.Time) =
## Sets the `file`'s last modification time. `OSError` is raised in case of
## an error.
when defined(posix):
let unixt = posix.Time(t.toUnix)
var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification]
if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
else:
let h = openHandle(path = file, writeAccess = true)
if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError())
var ft = t.toWinTime.toFILETIME
let res = setFileTime(h, nil, nil, ft.addr)
discard h.closeHandle
if res == 0'i32: raiseOSError(osLastError())

View File

@@ -1001,13 +1001,21 @@ elif not defined(useNimRtl):
{.pop}
proc close(p: Process) =
if p.inStream != nil: close(p.inStream)
if p.outStream != nil: close(p.outStream)
if p.errStream != nil: close(p.errStream)
if poParentStreams notin p.options:
discard close(p.inHandle)
discard close(p.outHandle)
discard close(p.errHandle)
if p.inStream != nil:
close(p.inStream)
else:
discard close(p.inHandle)
if p.outStream != nil:
close(p.outStream)
else:
discard close(p.outHandle)
if p.errStream != nil:
close(p.errStream)
else:
discard close(p.errHandle)
proc suspend(p: Process) =
if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
@@ -1281,7 +1289,7 @@ elif not defined(useNimRtl):
proc select(readfds: var seq[Process], timeout = 500): int =
var tv: Timeval
tv.tv_sec = 0
tv.tv_sec = posix.Time(0)
tv.tv_usec = timeout * 1000
var rd: TFdSet

View File

@@ -1647,11 +1647,15 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
let last = s.high
var i = 0
while true:
var j = find(a, s, sub, i, last)
let j = find(a, s, sub, i, last)
if j < 0: break
add result, substr(s, i, j - 1)
add result, by
i = j + len(sub)
if sub.len == 0:
if i < s.len: add result, s[i]
i = j + 1
else:
i = j + sub.len
# copy the rest:
add result, substr(s, i)
@@ -1680,6 +1684,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
initSkipTable(a, sub)
var i = 0
let last = s.high
let sublen = max(sub.len, 1)
while true:
var j = find(a, s, sub, i, last)
if j < 0: break
@@ -1688,7 +1693,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
(j+sub.len >= s.len or s[j+sub.len] notin wordChars):
add result, substr(s, i, j - 1)
add result, by
i = j + len(sub)
i = j + sublen
else:
add result, substr(s, i, j)
i = j + 1
@@ -2546,6 +2551,9 @@ when isMainModule:
doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc"
doAssert "oo".replace("", "abc") == "abcoabcoabc"
type MyEnum = enum enA, enB, enC, enuD, enE
doAssert parseEnum[MyEnum]("enu_D") == enuD

View File

@@ -557,8 +557,8 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
when defined(windows):
let h = conHandle(f)
var old = getAttributes(h) and not FOREGROUND_RGB
if bright:
old = old or FOREGROUND_INTENSITY
old = if bright: old or FOREGROUND_INTENSITY
else: old and not(FOREGROUND_INTENSITY)
const lookup: array[ForegroundColor, int] = [
0,
(FOREGROUND_RED),
@@ -579,8 +579,8 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
when defined(windows):
let h = conHandle(f)
var old = getAttributes(h) and not BACKGROUND_RGB
if bright:
old = old or BACKGROUND_INTENSITY
old = if bright: old or BACKGROUND_INTENSITY
else: old and not(BACKGROUND_INTENSITY)
const lookup: array[BackgroundColor, int] = [
0,
(BACKGROUND_RED),

View File

@@ -191,6 +191,7 @@ const
secondsInHour = 60*60
secondsInDay = 60*60*24
minutesInHour = 60
rateDiff = 10000000'i64 # 100 nsecs
# The number of hectonanoseconds between 1601/01/01 (windows epoch)
# and 1970/01/01 (unix epoch).
epochDiff = 116444736000000000'i64
@@ -352,7 +353,9 @@ proc `$`*(dur: Duration): string =
parts.add $quantity & " " & unitStrings[unit] & "s"
result = ""
if parts.len == 1:
if parts.len == 0:
result.add "0 nanoseconds"
elif parts.len == 1:
result = parts[0]
elif parts.len == 2:
result = parts[0] & " and " & parts[1]
@@ -371,6 +374,21 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
t.seconds
proc fromWinTime*(win: int64): Time =
## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
## to a ``Time``.
let hnsecsSinceEpoch = (win - epochDiff)
var seconds = hnsecsSinceEpoch div rateDiff
var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int
if nanos < 0:
nanos += convert(Seconds, Nanoseconds, 1)
seconds -= 1
result = initTime(seconds, nanos)
proc toWinTime*(t: Time): int64 =
## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
result = t.seconds * rateDiff + epochDiff + t.nanoseconds div 100
proc isLeapYear*(year: int): bool =
## Returns true if ``year`` is a leap year.
year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
@@ -853,10 +871,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
elif defined(windows):
var f: FILETIME
getSystemTimeAsFileTime(f)
let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100
let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch)
let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int
result = initTime(seconds, nanos)
result = fromWinTime(rdFileTime(f))
proc now*(): DateTime {.tags: [TimeEffect], benign.} =
## Get the current time as a ``DateTime`` in the local timezone.
@@ -1713,17 +1728,6 @@ when not defined(JS):
var
clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
const
rateDiff = 10000000'i64 # 100 nsecs
proc unixTimeToWinTime*(time: CTime): int64 =
## converts a UNIX `Time` (``time_t``) to a Windows file time
result = int64(time) * rateDiff + epochDiff
proc winTimeToUnixTime*(time: int64): CTime =
## converts a Windows time to a UNIX `Time` (``time_t``)
result = CTime((time - epochDiff) div rateDiff)
when not defined(useNimRtl):
proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
## gets time spent that the CPU spent to run the current process in
@@ -1748,7 +1752,7 @@ when not defined(JS):
when defined(posix):
var a: Timeval
gettimeofday(a)
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
elif defined(windows):
var f: winlean.FILETIME
getSystemTimeAsFileTime(f)
@@ -1765,6 +1769,19 @@ when defined(JS):
# Deprecated procs
when not defined(JS):
proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
## Converts a UNIX `Time` (``time_t``) to a Windows file time
##
## **Deprecated:** use ``toWinTime`` instead.
result = int64(time) * rateDiff + epochDiff
proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
## Converts a Windows time to a UNIX `Time` (``time_t``)
##
## **Deprecated:** use ``fromWinTime`` instead.
result = CTime((time - epochDiff) div rateDiff)
proc initInterval*(seconds, minutes, hours, days, months,
years: int = 0): TimeInterval {.deprecated.} =
## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.

View File

@@ -2404,7 +2404,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
if x.isNil and y.isNil:
return true
else:
when not defined(JS) or defined(nimphp):
when not defined(JS):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
result = cast[pointer](x)
else:
@@ -2766,17 +2766,14 @@ type
when defined(JS):
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
when defined(nimphp):
asm """`x` .= `y`;"""
else:
asm """
var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) {
`x`[0][len] = `y`.charCodeAt(i);
++len;
}
`x`[0][len] = 0
"""
asm """
var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) {
`x`[0][len] = `y`.charCodeAt(i);
++len;
}
`x`[0][len] = 0
"""
proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".}
elif hasAlloc:
@@ -4139,17 +4136,18 @@ template doAssertRaises*(exception, code: untyped): typed =
runnableExamples:
doAssertRaises(ValueError):
raise newException(ValueError, "Hello World")
var wrong = false
try:
block:
code
raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
code
wrong = true
except exception:
discard
except Exception as exc:
raiseAssert(astToStr(exception) &
" wasn't raised, another error was raised instead by:\n"&
astToStr(code))
if wrong:
raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code))
when defined(cpp) and appType != "lib" and not defined(js) and
not defined(nimscript) and hostOS != "standalone":

View File

@@ -79,8 +79,12 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
GenericSeqSize),
mt.base, shallow)
of tyObject:
if mt.base != nil:
genericAssignAux(dest, src, mt.base, shallow)
var it = mt.base
# don't use recursion here on the PNimType because the subtype
# check should only be done at the very end:
while it != nil:
genericAssignAux(dest, src, it.node, shallow)
it = it.base
genericAssignAux(dest, src, mt.node, shallow)
# we need to copy m_type field for tyObject, as it could be empty for
# sequence reallocations:
@@ -89,6 +93,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
# if p of TB:
# var tbObj = TB(p)
# tbObj of TC # needs to be false!
#c_fprintf(stdout, "%s %s\n", pint[].name, mt.name)
chckObjAsgn(cast[ptr PNimType](src)[], mt)
pint[] = mt # cast[ptr PNimType](src)[]
of tyTuple:
genericAssignAux(dest, src, mt.node, shallow)

View File

@@ -48,10 +48,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
result[0] = x
proc isNimException(): bool {.asmNoStackFrame.} =
when defined(nimphp):
asm "return isset(`lastJSError`['m_type']);"
else:
asm "return `lastJSError`.m_type;"
asm "return `lastJSError`.m_type;"
proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
if isNimException(): result = cast[ref Exception](lastJSError)
@@ -61,15 +58,14 @@ proc getCurrentExceptionMsg*(): string =
if isNimException():
return cast[Exception](lastJSError).msg
else:
when not defined(nimphp):
var msg: cstring
{.emit: """
if (`lastJSError`.message !== undefined) {
`msg` = `lastJSError`.message;
}
""".}
if not msg.isNil:
return $msg
var msg: cstring
{.emit: """
if (`lastJSError`.message !== undefined) {
`msg` = `lastJSError`.message;
}
""".}
if not msg.isNil:
return $msg
return ""
proc auxWriteStackTrace(f: PCallFrame): string =
@@ -140,12 +136,9 @@ proc raiseException(e: ref Exception, ename: cstring) {.
e.name = ename
if excHandler == 0:
unhandledException(e)
when defined(nimphp):
asm """throw new Exception($`e`["message"]);"""
else:
when NimStackTrace:
e.trace = rawWriteStackTrace()
asm "throw `e`;"
when NimStackTrace:
e.trace = rawWriteStackTrace()
asm "throw `e`;"
proc reraiseException() {.compilerproc, asmNoStackFrame.} =
if lastJSError == nil:
@@ -173,57 +166,35 @@ proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
raise newException(FieldError, f & " is not accessible")
proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
$args = func_get_args();
$result = array();
foreach ($args as $x) {
if (is_array($x)) {
for ($j = $x[0]; $j <= $x[1]; $j++) {
$result[$j] = true;
}
} else {
$result[$x] = true;
asm """
var result = {};
for (var i = 0; i < arguments.length; ++i) {
var x = arguments[i];
if (typeof(x) == "object") {
for (var j = x[0]; j <= x[1]; ++j) {
result[j] = true;
}
} else {
result[x] = true;
}
return $result;
"""
else:
asm """
var result = {};
for (var i = 0; i < arguments.length; ++i) {
var x = arguments[i];
if (typeof(x) == "object") {
for (var j = x[0]; j <= x[1]; ++j) {
result[j] = true;
}
} else {
result[x] = true;
}
}
return result;
"""
}
return result;
"""
proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `c`;""".}
else:
{.emit: """
var ln = `c`.length;
var result = new Array(ln + 1);
var i = 0;
for (; i < ln; ++i) {
result[i] = `c`.charCodeAt(i);
}
result[i] = 0; // terminating zero
return result;
""".}
{.emit: """
var ln = `c`.length;
var result = new Array(ln + 1);
var i = 0;
for (; i < ln; ++i) {
result[i] = `c`.charCodeAt(i);
}
result[i] = 0; // terminating zero
return result;
""".}
proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `c`;""".}
else:
{.emit: """
{.emit: """
var ln = `c`.length;
var result = new Array(ln);
var r = 0;
@@ -261,156 +232,93 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
""".}
proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
{.emit: """return `s`;""".}
else:
asm """
var len = `s`.length-1;
var asciiPart = new Array(len);
var fcc = String.fromCharCode;
var nonAsciiPart = null;
var nonAsciiOffset = 0;
for (var i = 0; i < len; ++i) {
if (nonAsciiPart !== null) {
var offset = (i - nonAsciiOffset) * 2;
var code = `s`[i].toString(16);
if (code.length == 1) {
code = "0"+code;
}
nonAsciiPart[offset] = "%";
nonAsciiPart[offset + 1] = code;
}
else if (`s`[i] < 128)
asciiPart[i] = fcc(`s`[i]);
else {
asciiPart.length = i;
nonAsciiOffset = i;
nonAsciiPart = new Array((len - i) * 2);
--i;
asm """
var len = `s`.length-1;
var asciiPart = new Array(len);
var fcc = String.fromCharCode;
var nonAsciiPart = null;
var nonAsciiOffset = 0;
for (var i = 0; i < len; ++i) {
if (nonAsciiPart !== null) {
var offset = (i - nonAsciiOffset) * 2;
var code = `s`[i].toString(16);
if (code.length == 1) {
code = "0"+code;
}
nonAsciiPart[offset] = "%";
nonAsciiPart[offset + 1] = code;
}
asciiPart = asciiPart.join("");
return (nonAsciiPart === null) ?
asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join(""));
else if (`s`[i] < 128)
asciiPart[i] = fcc(`s`[i]);
else {
asciiPart.length = i;
nonAsciiOffset = i;
nonAsciiPart = new Array((len - i) * 2);
--i;
}
}
asciiPart = asciiPart.join("");
return (nonAsciiPart === null) ?
asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join(""));
"""
proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return str_repeat(chr(0),`len`);
"""
else:
asm """
var result = new Array(`len`+1);
result[0] = 0;
result[`len`] = 0;
return result;
"""
when defined(nimphp):
proc nimAt(x: string; i: int): string {.asmNoStackFrame, compilerproc.} =
asm """
return `x`[`i`];
"""
when defined(nimphp):
proc nimSubstr(s: string; a, b: int): string {.
asmNoStackFrame, compilerproc.} =
asm """return substr(`s`,`a`,`b`-`a`+1);"""
asm """
var result = new Array(`len`+1);
result[0] = 0;
result[`len`] = 0;
return result;
"""
proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
# argument type is a fake
when defined(nimphp):
asm """
return count(`a`);
"""
else:
asm """
var result = 0;
for (var elem in `a`) { ++result; }
return result;
"""
asm """
var result = 0;
for (var elem in `a`) { ++result; }
return result;
"""
proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
foreach (`b` as $elem=>$_) { if (!isset(`a`[$elem])) return false; }
return true;
"""
else:
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
for (var elem in `b`) { if (!`a`[elem]) return false; }
return true;
"""
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
for (var elem in `b`) { if (!`a`[elem]) return false; }
return true;
"""
proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; }
return true;
"""
else:
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
return true;
"""
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
return true;
"""
proc SetLt(a, b: int): bool {.compilerproc.} =
result = SetLe(a, b) and not SetEq(a, b)
proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
var $result = array();
foreach (`a` as $elem=>$_) {
if (isset(`b`[$elem])) { $result[$elem] = true; }
}
return $result;
"""
else:
asm """
var result = {};
for (var elem in `a`) {
if (`b`[elem]) { result[elem] = true; }
}
return result;
"""
asm """
var result = {};
for (var elem in `a`) {
if (`b`[elem]) { result[elem] = true; }
}
return result;
"""
proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
var $result = array();
foreach (`a` as $elem=>$_) { $result[$elem] = true; }
foreach (`b` as $elem=>$_) { $result[$elem] = true; }
return $result;
"""
else:
asm """
var result = {};
for (var elem in `a`) { result[elem] = true; }
for (var elem in `b`) { result[elem] = true; }
return result;
"""
asm """
var result = {};
for (var elem in `a`) { result[elem] = true; }
for (var elem in `b`) { result[elem] = true; }
return result;
"""
proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
when defined(nimphp):
asm """
$result = array();
foreach (`a` as $elem=>$_) {
if (!isset(`b`[$elem])) { $result[$elem] = true; }
}
return $result;
"""
else:
asm """
var result = {};
for (var elem in `a`) {
if (!`b`[elem]) { result[elem] = true; }
}
return result;
"""
asm """
var result = {};
for (var elem in `a`) {
if (!`b`[elem]) { result[elem] = true; }
}
return result;
"""
proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
asm """
@@ -424,15 +332,8 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
return `a`.length - `b`.length;
"""
proc cmp(x, y: string): int =
when defined(nimphp):
asm """
if(`x` < `y`) `result` = -1;
elseif (`x` > `y`) `result` = 1;
else `result` = 0;
"""
else:
return cmpStrings(x, y)
proc cmp(x, y: string): int =
return cmpStrings(x, y)
proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
asm """
@@ -467,7 +368,7 @@ elif not defined(nimOldEcho):
console.log(buf);
"""
elif not defined(nimphp):
else:
proc ewriteln(x: cstring) =
var node : JSRef
{.emit: "`node` = document.getElementsByTagName('body')[0];".}
@@ -493,127 +394,77 @@ elif not defined(nimphp):
# Arithmetic:
proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` + `b`;
"""
else:
asm """
var result = `a` + `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
asm """
var result = `a` + `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` - `b`;
"""
else:
asm """
var result = `a` - `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
asm """
var result = `a` - `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` * `b`;
"""
else:
asm """
var result = `a` * `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
asm """
var result = `a` * `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return trunc(`a` / `b`);
"""
else:
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.trunc(`a` / `b`);
"""
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.trunc(`a` / `b`);
"""
proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` % `b`;
"""
else:
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.trunc(`a` % `b`);
"""
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.trunc(`a` % `b`);
"""
proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` + `b`;
"""
else:
asm """
var result = `a` + `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
asm """
var result = `a` + `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` - `b`;
"""
else:
asm """
var result = `a` - `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
asm """
var result = `a` - `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` * `b`;
"""
else:
asm """
var result = `a` * `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
asm """
var result = `a` * `b`;
if (result > 9223372036854775807
|| result < -9223372036854775808) `raiseOverflow`();
return result;
"""
proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return trunc(`a` / `b`);
"""
else:
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.trunc(`a` / `b`);
"""
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.trunc(`a` / `b`);
"""
proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
when defined(nimphp):
asm """
return `a` % `b`;
"""
else:
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.trunc(`a` % `b`);
"""
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.trunc(`a` % `b`);
"""
proc negInt(a: int): int {.compilerproc.} =
result = a*(-1)
@@ -767,24 +618,14 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
else:
discard
when defined(nimphp):
proc arrayConstr(len: int, value: string, typ: string): JSRef {.
asmNoStackFrame, compilerproc.} =
# types are fake
asm """
$result = array();
for ($i = 0; $i < `len`; $i++) $result[] = `value`;
return $result;
"""
else:
proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
asmNoStackFrame, compilerproc.} =
proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
asmNoStackFrame, compilerproc.} =
# types are fake
asm """
var result = new Array(`len`);
for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
return result;
"""
asm """
var result = new Array(`len`);
for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
return result;
"""
proc chckIndx(i, a, b: int): int {.compilerproc.} =
if i >= a and i <= b: return i

View File

@@ -1086,3 +1086,14 @@ proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, d
proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.}
proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".}
proc toFILETIME*(t: int64): FILETIME =
## Convert the Windows file time timestamp ``t`` to ``FILETIME``.
result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32))
type
LPFILETIME* = ptr FILETIME
proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME,
lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL
{.stdcall, dynlib: "kernel32", importc: "SetFileTime".}

View File

@@ -13,14 +13,8 @@ cat
cat
dog
dog
dog value
cat value
dog value
cat value
dog
dog
dog value
cat value
dog 1
dog 2
'''
@@ -243,11 +237,12 @@ reject modifiesCovariantArray(dogRefsArray.addr)
var dogValues = @[vdog, vdog]
var dogValuesArray = [vdog, vdog]
var animalValues = @[Animal(vdog), Animal(vcat)]
var animalValuesArray = [Animal(vdog), Animal(vcat)]
when false:
var animalValues = @[Animal(vdog), Animal(vcat)]
var animalValuesArray = [Animal(vdog), Animal(vcat)]
wantsNonCovariantSeq animalValues
wantsNonCovariantArray animalValuesArray
wantsNonCovariantSeq animalValues
wantsNonCovariantArray animalValuesArray
reject wantsNonCovariantSeq(dogRefs)
reject modifiesCovariantOperArray(dogRefs)
@@ -260,7 +255,6 @@ modifiesDerivedOperArray dogRefs
reject modifiesDerivedOperArray(dogValues)
reject modifiesDerivedOperArray(animalRefs)
wantsNonCovariantOperArray animalValues
reject wantsNonCovariantOperArray(animalRefs)
reject wantsNonCovariantOperArray(dogRefs)
reject wantsNonCovariantOperArray(dogValues)

11
tests/errmsgs/t1154.nim Normal file
View File

@@ -0,0 +1,11 @@
discard """
errormsg: "invalid type: 'expr' in this context: 'proc (a: varargs[expr])' for proc"
line: 8
"""
import typetraits
proc foo(a:varargs[expr]) =
echo a[0].type.name
foo(1)

View File

@@ -0,0 +1,9 @@
discard """
errormsg: "overloaded 'x' leads to ambiguous calls"
line: 9
"""
# bug #6393
proc x(): int = 7
proc x(): string = "strange"

View File

@@ -27,11 +27,19 @@ import strutils
template assertNot(arg: untyped): untyped =
assert(not(arg))
proc foo(arg: int): void =
discard
proc foo(arg: float): void =
discard
static:
## test eqIdent
let a = "abc_def"
let b = "abcDef"
let c = "AbcDef"
let d = nnkBracketExpr.newTree() # not an identifier at all
assert eqIdent( a , b )
assert eqIdent(newIdentNode(a), b )
@@ -62,3 +70,12 @@ static:
assertNot eqIdent(genSym(nskLet, c), newIdentNode( b))
assertNot eqIdent(newIdentNode( c), genSym(nskLet, b))
assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b))
# eqIdent on non identifier at all
assertNot eqIdent(a,d)
# eqIdent on sym choice
let fooSym = bindSym"foo"
assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice}
assert fooSym.eqIdent("fOO")
assertNot fooSym.eqIdent("bar")

View File

@@ -1,9 +1,7 @@
discard """
output: '''tbObj of TC false
false
output: '''tbObj of TC true
true
5
false'''
5'''
"""
# bug #1053
@@ -20,10 +18,10 @@ type
proc test(p: TA) =
#echo "p of TB ", p of TB
if p of TB:
var tbObj = TB(p)
#var tbObj = TB(p)
# tbObj is actually no longer compatible with TC:
echo "tbObj of TC ", tbObj of TC
echo "tbObj of TC ", p of TC
var v = TC()
v.a = 1
@@ -48,8 +46,8 @@ proc isMyObject(obj: TObject) =
asd.x = 5
var asdCopy = TObject(asd)
echo asdCopy of MyObject
#var asdCopy = TObject(asd)
#echo asdCopy of MyObject
isMyObject(asd)
isMyObject(asdCopy)
#isMyObject(asdCopy)

View File

@@ -0,0 +1,24 @@
discard """
outputsub: '''ObjectAssignmentError'''
exitcode: "1"
"""
# bug #7637
type
Fruit = object of RootObj
name*: string
Apple = object of Fruit
Pear = object of Fruit
method eat(f: Fruit) {.base.} =
raise newException(Exception, "PURE VIRTUAL CALL")
method eat(f: Apple) =
echo "fruity"
method eat(f: Pear) =
echo "juicy"
let basket = [Apple(name:"a"), Pear(name:"b")]
eat(basket[0])

View File

@@ -9,8 +9,8 @@ discard """
(k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ())
(k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ())
(k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ())
(x: 123)
(x: 123)
(y: 0, x: 123)
(y: 678, x: 123)
(z: 89, y: 0, x: 128)
(y: 678, x: 123)
(y: 678, x: 123)
@@ -33,7 +33,6 @@ type
`method`: TEmpty # bug #1791
proc `$`[T](s: seq[T]): string =
# XXX why is that not in the stdlib?
result = "["
for i, x in s:
if i > 0: result.add(", ")
@@ -59,7 +58,7 @@ type
# inherited fields are ignored, so no fields are set
when true:
var
o: A
o: B
o = B(x: 123)
echo o
o = B(y: 678, x: 123)

24
tests/osproc/tclose.nim Normal file
View File

@@ -0,0 +1,24 @@
discard """
exitcode: 0
"""
when defined(linux):
import osproc, os
proc countFds(): int =
result = 0
for i in walkDir("/proc/self/fd"):
result += 1
let initCount = countFds()
let p = osproc.startProcess("echo", options={poUsePath})
assert countFds() == initCount + 3
p.close
assert countFds() == initCount
let p1 = osproc.startProcess("echo", options={poUsePath})
discard p1.inputStream
assert countFds() == initCount + 3
p.close
assert countFds() == initCount

View File

@@ -11,7 +11,7 @@ proc parseInt(x: float): int {.noSideEffect.} = discard
proc parseInt(x: bool): int {.noSideEffect.} = discard
proc parseInt(x: float32): int {.noSideEffect.} = discard
proc parseInt(x: int8): int {.noSideEffect.} = discard
proc parseInt(x: TFile): int {.noSideEffect.} = discard
proc parseInt(x: File): int {.noSideEffect.} = discard
proc parseInt(x: char): int {.noSideEffect.} = discard
proc parseInt(x: int16): int {.noSideEffect.} = discard

View File

@@ -0,0 +1,35 @@
discard """
output: '''true'''
"""
# bug #7638
import threadpool, os, strformat
proc timer(d: int): int =
#echo fmt"sleeping {d}"
sleep(d)
#echo fmt"done {d}"
return d
var durations = [1000, 2000, 3000, 4000, 5000]
var tasks: seq[FlowVarBase] = @[]
var results: seq[int] = @[]
for i in 0 .. durations.high:
tasks.add spawn timer(durations[i])
var index = awaitAny(tasks)
while index != -1:
results.add ^cast[FlowVar[int]](tasks[index])
tasks.del(index)
#echo repr results
index = awaitAny(tasks)
doAssert results.len == 5
doAssert 1000 in results
doAssert 2000 in results
doAssert 3000 in results
doAssert 4000 in results
doAssert 5000 in results
sync()
echo "true"

View File

@@ -51,6 +51,9 @@ block: # A bit more advanced case
static: assert(hasCustomPragma(myproc, alternativeKey))
const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
static: assert(hasFieldCustomPragma == false)
# pragma on an object
static:
assert Subfield.hasCustomPragma(defaultValue)
@@ -71,6 +74,8 @@ block: # ref types
MyFile {.defaultValue: "closed".} = ref object
path {.defaultValue: "invalid".}: string
TypeWithoutPragma = object
var s = NodeRef()
const
@@ -91,7 +96,7 @@ block: # ref types
var ptrS = NodePtr(nil)
const
ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey)
ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
static:
assert ptrRightSerKey == "r"
@@ -103,6 +108,9 @@ block: # ref types
assert fileDefVal == "closed"
assert filePathDefVal == "invalid"
static:
assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
block:
type
VariantKind = enum

View File

@@ -409,4 +409,11 @@ suite "ttimes":
# Bug with adding a day to a Time
let day = 24.hours
let tomorrow = now + day
check tomorrow - now == initDuration(days = 1)
check tomorrow - now == initDuration(days = 1)
test "fromWinTime/toWinTime":
check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0
check (-1).fromWinTime.nanoseconds == convert(Seconds, Nanoseconds, 1) - 100
check -1.fromWinTime.toWinTime == -1
# One nanosecond is discarded due to differences in time resolution
check initTime(0, 101).toWinTime.fromWinTime.nanoseconds == 100

1
tests/template/i2416.nim Normal file
View File

@@ -0,0 +1 @@
template i2416*() = echo "i2416"

2
tests/template/t2416.nim Normal file
View File

@@ -0,0 +1,2 @@
import i2416
i2416()

View File

@@ -0,0 +1,9 @@
discard """
line: 9
errormsg: "address of 'result' may not escape its stack frame"
"""
# bug #5113
proc makeSeqVar(size: Natural): var seq[int] =
newSeq(result, size)

View File

@@ -24,7 +24,7 @@ const
Usage:
nimpretty [options] file.nim
Options:
--backup:ON|OFF create a backup file before overwritting (default: ON)
--backup:on|off create a backup file before overwritting (default: ON)
--version show the version
--help show this help
"""
@@ -43,7 +43,7 @@ proc prettyPrint(infile: string) =
let fileIdx = fileInfoIdx(infile)
let tree = parseFile(fileIdx, newIdentCache())
let outfile = changeFileExt(infile, ".pretty.nim")
renderModule(tree, infile, outfile, {})
renderModule(tree, infile, outfile, {}, fileIdx)
proc main =
var infile: string

View File

@@ -31,7 +31,7 @@ file: ticker.html
[Documentation]
doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst"
doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt"
doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst"
doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst;manual/*.rst"
pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst"
srcdoc2: "system.nim;system/nimscript;pure/ospaths"
srcdoc2: "core/macros;pure/marshal;core/typeinfo"