Resolve conflic.

This commit is contained in:
Konstantin Molchanov 2017-03-27 21:46:43 +04:00
commit 9a67d0c1e4
52 changed files with 666 additions and 234 deletions

View file

@ -82,7 +82,7 @@ template mdbg*: bool {.dirty.} =
elif compiles(L.fileIdx): elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx L.fileIdx == gProjectMainIdx
else: else:
false error()
# --------------------------- ident tables ---------------------------------- # --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef proc idTableGet*(t: TIdTable, key: PIdObj): RootRef

View file

@ -604,6 +604,7 @@ proc generateHeaders(m: BModule) =
else: else:
addf(m.s[cfsHeaders], "#include $1$N", [rope(it)]) addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
add(m.s[cfsHeaders], "#undef linux" & tnl) add(m.s[cfsHeaders], "#undef linux" & tnl)
add(m.s[cfsHeaders], "#undef near" & tnl)
proc initFrame(p: BProc, procname, filename: Rope): Rope = proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame") discard cgsym(p.module, "nimFrame")

View file

@ -229,6 +229,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
var base = lastSon(methods[0].ast).sym var base = lastSon(methods[0].ast).sym
result = base result = base
var paramLen = sonsLen(base.typ) var paramLen = sonsLen(base.typ)
var nilchecks = newNodeI(nkStmtList, base.info)
var disp = newNodeI(nkIfStmt, base.info) var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysSym("and") var ands = getSysSym("and")
var iss = getSysSym("of") var iss = getSysSym("of")
@ -239,7 +240,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
if contains(relevantCols, col): if contains(relevantCols, col):
var isn = newNodeIT(nkCall, base.info, getSysType(tyBool)) var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
addSon(isn, newSymNode(iss)) addSon(isn, newSymNode(iss))
addSon(isn, newSymNode(base.typ.n.sons[col].sym)) let param = base.typ.n.sons[col].sym
addSon(isn, newSymNode(param))
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
addSon(nilchecks, newTree(nkCall,
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil: if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
@ -270,7 +275,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(disp, a) addSon(disp, a)
else: else:
disp = ret disp = ret
result.ast.sons[bodyPos] = disp nilchecks.add disp
result.ast.sons[bodyPos] = nilchecks
proc generateMethodDispatchers*(g: ModuleGraph): PNode = proc generateMethodDispatchers*(g: ModuleGraph): PNode =
result = newNode(nkStmtList) result = newNode(nkStmtList)

View file

@ -785,14 +785,24 @@ proc writeJsonBuildInstructions*(projectfile: string) =
else: else:
lit "],\L" lit "],\L"
proc linkfiles(f: File; buf, objfiles: var string; toLink: seq[string]) = proc linkfiles(f: File; buf, objfiles: var string) =
for i, it in toLink: for i, it in externalToLink:
let objfile = addFileExt(it, CC[cCompiler].objExt) let
str(objfile) objFile = if noAbsolutePaths(): it.extractFilename else: it
objStr = addFileExt(objFile, CC[cCompiler].objExt)
add(objfiles, ' ') add(objfiles, ' ')
add(objfiles, quoteShell(objfile)) add(objfiles, objStr)
str objStr
if i == toLink.high: if toCompile.len == 0 and i == externalToLink.high:
lit "\L"
else:
lit ",\L"
for i, x in toCompile:
let objStr = quoteShell(x.obj)
add(objfiles, ' ')
add(objfiles, objStr)
str objStr
if i == toCompile.high:
lit "\L" lit "\L"
else: else:
lit ",\L" lit ",\L"
@ -809,7 +819,7 @@ proc writeJsonBuildInstructions*(projectfile: string) =
lit "],\L\"link\":[\L" lit "],\L\"link\":[\L"
var objfiles = "" var objfiles = ""
# XXX add every file here that is to link # XXX add every file here that is to link
linkfiles(f, buf, objfiles, externalToLink) linkfiles(f, buf, objfiles)
lit "],\L\"linkcmd\": " lit "],\L\"linkcmd\": "
str getLinkCmd(projectfile, objfiles) str getLinkCmd(projectfile, objfiles)

View file

@ -15,17 +15,28 @@ import
proc ensureNoMissingOrUnusedSymbols(scope: PScope) proc ensureNoMissingOrUnusedSymbols(scope: PScope)
proc considerQuotedIdent*(n: PNode): PIdent = proc noidentError(n, origin: PNode) =
var m = ""
if origin != nil:
m.add "in expression '" & origin.renderTree & "': "
m.add "identifier expected, but found '" & n.renderTree & "'"
localError(n.info, m)
proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
## Retrieve a PIdent from a PNode, taking into account accent nodes. ## Retrieve a PIdent from a PNode, taking into account accent nodes.
## ``origin`` can be nil. If it is not nil, it is used for a better
## error message.
template handleError(n, origin: PNode) =
noidentError(n, origin)
result = getIdent"<Error>"
case n.kind case n.kind
of nkIdent: result = n.ident of nkIdent: result = n.ident
of nkSym: result = n.sym.name of nkSym: result = n.sym.name
of nkAccQuoted: of nkAccQuoted:
case n.len case n.len
of 0: of 0: handleError(n, origin)
localError(n.info, errIdentifierExpected, renderTree(n)) of 1: result = considerQuotedIdent(n.sons[0], origin)
result = getIdent"<Error>"
of 1: result = considerQuotedIdent(n.sons[0])
else: else:
var id = "" var id = ""
for i in 0.. <n.len: for i in 0.. <n.len:
@ -33,14 +44,11 @@ proc considerQuotedIdent*(n: PNode): PIdent =
case x.kind case x.kind
of nkIdent: id.add(x.ident.s) of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s) of nkSym: id.add(x.sym.name.s)
else: else: handleError(n, origin)
localError(n.info, errIdentifierExpected, renderTree(n))
return getIdent"<Error>"
result = getIdent(id) result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else: else:
localError(n.info, errIdentifierExpected, renderTree(n)) handleError(n, origin)
result = getIdent"<Error>"
template addSym*(scope: PScope, s: PSym) = template addSym*(scope: PScope, s: PSym) =
strTableAdd(scope.symbols, s) strTableAdd(scope.symbols, s)
@ -353,7 +361,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.sons[1].kind == nkIdent: if n.sons[1].kind == nkIdent:
ident = n.sons[1].ident ident = n.sons[1].ident
elif n.sons[1].kind == nkAccQuoted: elif n.sons[1].kind == nkAccQuoted:
ident = considerQuotedIdent(n.sons[1]) ident = considerQuotedIdent(n.sons[1], n)
if ident != nil: if ident != nil:
if o.m == c.module: if o.m == c.module:
# a module may access its private members: # a module may access its private members:
@ -363,8 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
else: else:
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n) result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
else: else:
localError(n.sons[1].info, errIdentifierExpected, noidentError(n.sons[1], n)
renderTree(n.sons[1]))
result = errorSym(c, n.sons[1]) result = errorSym(c, n.sons[1])
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
o.mode = oimSymChoice o.mode = oimSymChoice

View file

@ -272,7 +272,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
if msgs.gErrorCounter == 0 and if msgs.gErrorCounter == 0 and
gCmd notin {cmdInterpret, cmdRun, cmdDump}: gCmd notin {cmdInterpret, cmdRun, cmdDump}:
when declared(system.getMaxMem): when declared(system.getMaxMem):
let usedMem = formatSize(getMaxMem()) & " peekmem" let usedMem = formatSize(getMaxMem()) & " peakmem"
else: else:
let usedMem = formatSize(getTotalMem()) let usedMem = formatSize(getTotalMem())
rawMessage(hintSuccessX, [$gLinesCompiled, rawMessage(hintSuccessX, [$gLinesCompiled,

View file

@ -355,7 +355,7 @@ proc semOpAux(c: PContext, n: PNode) =
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkExprEqExpr and sonsLen(a) == 2: if a.kind == nkExprEqExpr and sonsLen(a) == 2:
var info = a.sons[0].info var info = a.sons[0].info
a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0], a), info)
a.sons[1] = semExprWithType(c, a.sons[1], flags) a.sons[1] = semExprWithType(c, a.sons[1], flags)
a.typ = a.sons[1].typ a.typ = a.sons[1].typ
else: else:
@ -1076,7 +1076,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
#restoreOldStyleType(n.sons[0]) #restoreOldStyleType(n.sons[0])
var i = considerQuotedIdent(n.sons[1]) var i = considerQuotedIdent(n.sons[1], n)
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
@ -1160,7 +1160,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
addSon(result, n.sons[1]) addSon(result, n.sons[1])
addSon(result, copyTree(n[0])) addSon(result, copyTree(n[0]))
else: else:
var i = considerQuotedIdent(n.sons[1]) var i = considerQuotedIdent(n.sons[1], n)
result = newNodeI(nkDotCall, n.info) result = newNodeI(nkDotCall, n.info)
result.flags.incl nfDotField result.flags.incl nfDotField
addSon(result, newIdentNode(i, n[1].info)) addSon(result, newIdentNode(i, n[1].info))
@ -1280,7 +1280,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
c.p.bracketExpr = oldBracketExpr c.p.bracketExpr = oldBracketExpr
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(a[1]) var id = considerQuotedIdent(a[1], a)
var setterId = newIdentNode(getIdent(id.s & '='), n.info) var setterId = newIdentNode(getIdent(id.s & '='), n.info)
# a[0] is already checked for semantics, that does ``builtinFieldAccess`` # a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
@ -1433,7 +1433,6 @@ proc semReturn(c: PContext, n: PNode): PNode =
proc semProcBody(c: PContext, n: PNode): PNode = proc semProcBody(c: PContext, n: PNode): PNode =
openScope(c) openScope(c)
result = semExpr(c, n) result = semExpr(c, n)
if c.p.resultSym != nil and not isEmptyType(result.typ): if c.p.resultSym != nil and not isEmptyType(result.typ):
# transform ``expr`` to ``result = expr``, but not if the expr is already # transform ``expr`` to ``result = expr``, but not if the expr is already
@ -1530,7 +1529,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
checkSonsLen(n, 2) checkSonsLen(n, 2)
var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope) var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
if m != nil and m.kind == skModule: if m != nil and m.kind == skModule:
let ident = considerQuotedIdent(n[1]) let ident = considerQuotedIdent(n[1], n)
if m == c.module: if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident) result = strTableGet(c.topLevelScope.symbols, ident)
else: else:
@ -1549,7 +1548,7 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
# we replace this node by a 'true' or 'false' node: # we replace this node by a 'true' or 'false' node:
result = newIntNode(nkIntLit, 0) result = newIntNode(nkIntLit, 0)
if not onlyCurrentScope and considerQuotedIdent(n[0]).s == "defined": if not onlyCurrentScope and considerQuotedIdent(n[0], n).s == "defined":
if n.sons[1].kind != nkIdent: if n.sons[1].kind != nkIdent:
localError(n.info, "obsolete usage of 'defined', use 'declared' instead") localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
elif condsyms.isDefined(n.sons[1].ident): elif condsyms.isDefined(n.sons[1].ident):
@ -2084,9 +2083,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n.sons[0], nil) var t = semTypeNode(c, n.sons[0], nil)
result = n result = newNodeIT(nkObjConstr, n.info, t)
result.typ = t result.add n.sons[0]
result.kind = nkObjConstr
t = skipTypes(t, {tyGenericInst, tyAlias}) t = skipTypes(t, {tyGenericInst, tyAlias})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
if t.kind != tyObject: if t.kind != tyObject:
@ -2099,7 +2097,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
if it.kind != nkExprColonExpr: if it.kind != nkExprColonExpr:
localError(n.info, errNamedExprExpected) localError(n.info, errNamedExprExpected)
break break
let id = considerQuotedIdent(it.sons[0]) let id = considerQuotedIdent(it.sons[0], it)
if containsOrIncl(ids, id.id): if containsOrIncl(ids, id.id):
localError(it.info, errFieldInitTwice, id.s) localError(it.info, errFieldInitTwice, id.s)
@ -2125,6 +2123,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
localError(it.info, errUndeclaredFieldX, id.s) localError(it.info, errUndeclaredFieldX, id.s)
it.sons[1] = e it.sons[1] = e
result.add it
# XXX object field name check for 'case objects' if the kind is static? # XXX object field name check for 'case objects' if the kind is static?
if tfNeedsInit in objType.flags: if tfNeedsInit in objType.flags:
while true: while true:

View file

@ -51,10 +51,10 @@ template macroToExpand(s): untyped =
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags) s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
template macroToExpandSym(s): untyped = template macroToExpandSym(s): untyped =
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
ctx: var GenericCtx): PNode = ctx: var GenericCtx; fromDotExpr=false): PNode =
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
case s.kind case s.kind
@ -145,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
elif s.name.id in ctx.toMixin: elif s.name.id in ctx.toMixin:
result = newDot(result, symChoice(c, n, s, scForceOpen)) result = newDot(result, symChoice(c, n, s, scForceOpen))
else: else:
let syms = semGenericStmtSymbol(c, n, s, ctx) let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
if syms.kind == nkSym: if syms.kind == nkSym:
let choice = symChoice(c, n, s, scForceOpen) let choice = symChoice(c, n, s, scForceOpen)
choice.kind = nkClosedSymChoice choice.kind = nkClosedSymChoice

View file

@ -125,6 +125,11 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
if n.sons[bodyPos].kind != nkEmpty: if n.sons[bodyPos].kind != nkEmpty:
let procParams = result.typ.n
for i in 1 .. <procParams.len:
addDecl(c, procParams[i].sym)
maybeAddResult(c, result, result.ast)
inc c.inGenericInst inc c.inGenericInst
# add it here, so that recursive generic procs are possible: # add it here, so that recursive generic procs are possible:
var b = n.sons[bodyPos] var b = n.sons[bodyPos]
@ -147,13 +152,17 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
for i in countup(0, c.generics.len - 1): for i in countup(0, c.generics.len - 1):
if c.generics[i].genericSym.id == s.id: if c.generics[i].genericSym.id == s.id:
var oldPrc = c.generics[i].inst.sym var oldPrc = c.generics[i].inst.sym
pushProcCon(c, oldPrc)
pushOwner(c, oldPrc)
pushInfoContext(oldPrc.info) pushInfoContext(oldPrc.info)
openScope(c) openScope(c)
var n = oldPrc.ast var n = oldPrc.ast
n.sons[bodyPos] = copyTree(s.getBody) n.sons[bodyPos] = copyTree(s.getBody)
instantiateBody(c, n, nil, oldPrc, s) instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
closeScope(c) closeScope(c)
popInfoContext() popInfoContext()
popOwner(c)
popProcCon(c)
proc sideEffectsCheck(c: PContext, s: PSym) = proc sideEffectsCheck(c: PContext, s: PSym) =
when false: when false:
@ -229,7 +238,6 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
skipIntLiteralParams(result) skipIntLiteralParams(result)
prc.typ = result prc.typ = result
maybeAddResult(c, prc, prc.ast)
popInfoContext() popInfoContext()
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,

View file

@ -160,10 +160,11 @@ proc discardCheck(c: PContext, result: PNode) =
else: else:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
var s = "expression '" & $n & "' is of type '" &
result.typ.typeToString & "' and has to be discarded"
if result.typ.kind == tyProc: if result.typ.kind == tyProc:
localError(n.info, "value of type '" & result.typ.typeToString & "' has to be discarded; for a function call use ()") s.add "; for a function call use ()"
else: localError(n.info, s)
localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode = proc semIf(c: PContext, n: PNode): PNode =
result = n result = n

View file

@ -350,6 +350,14 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
else: result = isIntConv else: result = isIntConv
else: result = isNone else: result = isNone
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
if fGenericOrigin != nil and last.kind == tyGenericInst and
last.len-1 == fGenericOrigin.len:
for i in countup(1, sonsLen(fGenericOrigin) - 1):
let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
if x == nil:
put(c, fGenericOrigin.sons[i], last.sons[i])
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a var t = a
assert t.kind == tyObject assert t.kind == tyObject
@ -363,12 +371,7 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
t = skipTypes(t, skipPtrs) t = skipTypes(t, skipPtrs)
inc depth inc depth
if t != nil: if t != nil:
if fGenericOrigin != nil and last.kind == tyGenericInst and genericParamPut(c, last, fGenericOrigin)
last.len-1 == fGenericOrigin.len:
for i in countup(1, sonsLen(fGenericOrigin) - 1):
let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
if x == nil:
put(c, fGenericOrigin.sons[i], last.sons[i])
result = depth result = depth
else: else:
result = -1 result = -1
@ -398,7 +401,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
break break
if r.kind == tyObject and ptrs <= 1: result = r if r.kind == tyObject and ptrs <= 1: result = r
proc isGenericSubtype(a, f: PType, d: var int): bool = proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType = nil): bool =
assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
var askip = skippedNone var askip = skippedNone
var fskip = skippedNone var fskip = skippedNone
@ -406,14 +409,17 @@ proc isGenericSubtype(a, f: PType, d: var int): bool =
let r = f.skipToObject(fskip) let r = f.skipToObject(fskip)
if r == nil: return false if r == nil: return false
var depth = 0 var depth = 0
var last = a
# XXX sameObjectType can return false here. Need to investigate # XXX sameObjectType can return false here. Need to investigate
# why that is but sameObjectType does way too much work here anyway. # why that is but sameObjectType does way too much work here anyway.
while t != nil and r.sym != t.sym and askip == fskip: while t != nil and r.sym != t.sym and askip == fskip:
t = t.sons[0] t = t.sons[0]
if t != nil: t = t.skipToObject(askip) if t == nil: break
else: break last = t
t = t.skipToObject(askip)
inc depth inc depth
if t != nil and askip == fskip: if t != nil and askip == fskip:
genericParamPut(c, last, fGenericOrigin)
d = depth d = depth
result = true result = true
@ -999,7 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# simply no match for now: # simply no match for now:
discard discard
elif x.kind == tyGenericInst and elif x.kind == tyGenericInst and
((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) and ((f.sons[0] == x.sons[0]) or isGenericSubType(c, x, f, depth)) and
(sonsLen(x) - 1 == sonsLen(f)): (sonsLen(x) - 1 == sonsLen(f)):
for i in countup(1, sonsLen(f) - 1): for i in countup(1, sonsLen(f) - 1):
if x.sons[i].kind == tyGenericParam: if x.sons[i].kind == tyGenericParam:
@ -1039,6 +1045,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
put(c, f.sons[i], x) put(c, f.sons[i], x)
if result == isNone:
# Here object inheriting from generic/specialized generic object
# crossing path with metatypes/aliases, so we need to separate them
# by checking sym.id
let genericSubtype = isGenericSubType(c, x, f, depth, f)
if not (genericSubtype and aobj.sym.id != fobj.sym.id):
depth = -1
if depth >= 0: if depth >= 0:
c.inheritancePenalty += depth c.inheritancePenalty += depth
# bug #4863: We still need to bind generic alias crap, so # bug #4863: We still need to bind generic alias crap, so

View file

@ -1394,6 +1394,27 @@ slice's bounds can hold any value supported by
their type, but it is the proc using the slice object which defines what values their type, but it is the proc using the slice object which defines what values
are accepted. are accepted.
To understand some of the different ways of specifying the indices of strings, arrays, sequences, etc.,
it must be remembered that Nim uses zero-based indices.
So the string ``b`` is of length 19, and two different ways of specifying the indices are
.. code-block:: nim
"Slices are useless."
| | |
0 11 17 using indices
^19 ^8 ^2 using ^ syntax
where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..<b.len]``, and it can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``
In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful"
``b[11..^2]`` is the portion "useless", and
``b[11..^2] = "useful"`` replaces the "useless" portion with "useful",
giving the result "Slices are useful."
Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or as ``b[11..b.len-2] = "useful"`` or as ``b[11..<b.len-1] = "useful"`` or as ....
Tuples Tuples
------ ------

View file

@ -41,30 +41,26 @@ Options:
Possible Commands: Possible Commands:
boot [options] bootstraps with given command line options boot [options] bootstraps with given command line options
distrohelper [bindir] helper for distro packagers distrohelper [bindir] helper for distro packagers
geninstall generate ./install.sh; Unix only!
testinstall test tar.xz package; Unix only! Only for devs!
clean cleans Nim project; removes generated files
web [options] generates the website and the full documentation
website [options] generates only the website
csource [options] builds the C sources for installation
pdf builds the PDF documentation
zip builds the installation ZIP package
xz builds the installation XZ package
nsis [options] builds the NSIS Setup installer (for Windows)
tests [options] run the testsuite
temp options creates a temporary compiler for testing
winrelease creates a release (for coredevs only)
nimble builds the Nimble tool
tools builds Nim related tools tools builds Nim related tools
pushcsource push generated C sources to its repo! Only for devs! nimble builds the Nimble tool
Boot options: Boot options:
-d:release produce a release version of the compiler -d:release produce a release version of the compiler
-d:tinyc include the Tiny C backend (not supported on Windows)
-d:useLinenoise use the linenoise library for interactive mode -d:useLinenoise use the linenoise library for interactive mode
(not needed on Windows) (not needed on Windows)
-d:nativeStacktrace use native stack traces (only for Mac OS X or Linux)
-d:noCaas build Nim without CAAS support
-d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups -d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups
Commands for core developers:
web [options] generates the website and the full documentation
website [options] generates only the website
csource -d:release builds the C sources for installation
pdf builds the PDF documentation
zip builds the installation zip package
xz builds the installation tar.xz package
testinstall test tar.xz package; Unix only!
tests [options] run the testsuite
temp options creates a temporary compiler for testing
winrelease creates a Windows release
pushcsource push generated C sources to its repo
Web options: Web options:
--googleAnalytics:UA-... add the given google analytics code to the docs. To --googleAnalytics:UA-... add the given google analytics code to the docs. To
build the official docs, use UA-48159761-1 build the official docs, use UA-48159761-1
@ -218,11 +214,14 @@ proc bundleNimsuggest(buildExe: bool) =
copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe) copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
removeFile("nimsuggest/nimsuggest".exe) removeFile("nimsuggest/nimsuggest".exe)
proc buildVccTool() =
nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")
proc bundleWinTools() = proc bundleWinTools() =
nimexec("c tools/finish.nim") nimexec("c tools/finish.nim")
copyExe("tools/finish".exe, "finish".exe) copyExe("tools/finish".exe, "finish".exe)
removeFile("tools/finish".exe) removeFile("tools/finish".exe)
nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") buildVccTool()
nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim") nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
when false: when false:
# not yet a tool worth including # not yet a tool worth including
@ -257,6 +256,7 @@ proc buildTools(latest: bool) =
let nimgrepExe = "bin/nimgrep".exe let nimgrepExe = "bin/nimgrep".exe
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
when defined(windows): buildVccTool()
buildNimble(latest) buildNimble(latest)
proc nsis(args: string) = proc nsis(args: string) =

View file

@ -173,17 +173,33 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
rawExec(db, query, args) rawExec(db, query, args)
var sqlres = mysql.useResult(db) var sqlres = mysql.useResult(db)
if sqlres != nil: if sqlres != nil:
var L = int(mysql.numFields(sqlres)) var
var result = newRow(L) L = int(mysql.numFields(sqlres))
var row: cstringArray row: cstringArray
result: Row
backup: Row
newSeq(result, L)
while true: while true:
row = mysql.fetchRow(sqlres) row = mysql.fetchRow(sqlres)
if row == nil: break if row == nil: break
for i in 0..L-1: for i in 0..L-1:
setLen(result[i], 0)
if row[i] == nil: if row[i] == nil:
if backup == nil:
newSeq(backup, L)
if backup[i] == nil and result[i] != nil:
shallowCopy(backup[i], result[i])
result[i] = nil result[i] = nil
else: else:
if result[i] == nil:
if backup != nil:
if backup[i] == nil:
backup[i] = ""
shallowCopy(result[i], backup[i])
setLen(result[i], 0)
else:
result[i] = ""
else:
setLen(result[i], 0)
add(result[i], row[i]) add(result[i], row[i])
yield result yield result
properFreeResult(sqlres, row) properFreeResult(sqlres, row)

View file

@ -406,6 +406,8 @@ proc setInterval*(w: Window, function: proc (), pause: int): ref TInterval
proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval
proc stop*(w: Window) proc stop*(w: Window)
proc requestAnimationFrame*(w: Window, function: proc (time: float)): int
proc cancelAnimationFrame*(w: Window, id: int)
# Node "methods" # Node "methods"
proc appendChild*(n, child: Node) proc appendChild*(n, child: Node)

View file

@ -489,3 +489,13 @@ proc writeFromStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} =
await f.write(value) await f.write(value)
else: else:
break break
proc readToStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} =
## Writes data to the specified future stream as the file is read.
while true:
let data = await read(f, 4000)
if data.len == 0:
break
await fs.write(data)
fs.complete()

View file

@ -80,7 +80,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
## Access the i-th element of `deq` by order from first to last. ## Access the i-th element of `deq` by order from first to last.
## deq[0] is the first, deq[^1] is the last. ## deq[0] is the first, deq[^1] is the last.
xBoundsCheck(deq, i) xBoundsCheck(deq, i)
return deq.data[(deq.first + i) and deq.mask] return deq.data[(deq.head + i) and deq.mask]
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
## Access the i-th element of `deq` and returns a mutable ## Access the i-th element of `deq` and returns a mutable

View file

@ -617,20 +617,6 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
flushFile(d) flushFile(d)
close(d) close(d)
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
when defined(Windows):
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
else:
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
else:
if c_rename(source, dest) != 0'i32:
raiseOSError(osLastError(), $strerror(errno))
when not declared(ENOENT) and not defined(Windows): when not declared(ENOENT) and not defined(Windows):
when NoFakeVars: when NoFakeVars:
const ENOENT = cint(2) # 2 on most systems including Solaris const ENOENT = cint(2) # 2 on most systems including Solaris
@ -647,25 +633,63 @@ when defined(Windows):
template setFileAttributes(file, attrs: untyped): untyped = template setFileAttributes(file, attrs: untyped): untyped =
setFileAttributesA(file, attrs) setFileAttributesA(file, attrs)
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## Removes the `file`. If this fails, `OSError` is raised. This does not fail ## Removes the `file`. If this fails, returns `false`. This does not fail
## if the file never existed in the first place. ## if the file never existed in the first place.
## On Windows, ignores the read-only attribute. ## On Windows, ignores the read-only attribute.
result = true
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
let f = newWideCString(file) let f = newWideCString(file)
else: else:
let f = file let f = file
if deleteFile(f) == 0: if deleteFile(f) == 0:
if getLastError() == ERROR_ACCESS_DENIED: result = false
if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0: let err = getLastError()
raiseOSError(osLastError()) if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
if deleteFile(f) == 0: result = true
raiseOSError(osLastError()) elif err == ERROR_ACCESS_DENIED and
setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
deleteFile(f) != 0:
result = true
else: else:
if c_remove(file) != 0'i32 and errno != ENOENT: if c_remove(file) != 0'i32 and errno != ENOENT:
result = false
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
## Removes the `file`. If this fails, `OSError` is raised. This does not fail
## if the file never existed in the first place.
## On Windows, ignores the read-only attribute.
if not tryRemoveFile(file):
when defined(Windows):
raiseOSError(osLastError())
else:
raiseOSError(osLastError(), $strerror(errno)) raiseOSError(osLastError(), $strerror(errno))
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
when defined(Windows):
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
else:
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
else:
if c_rename(source, dest) != 0'i32:
let err = osLastError()
if err == EXDEV.OSErrorCode:
# Fallback to copy & del
copyFile(source, dest)
try:
removeFile(source)
except:
discard tryRemoveFile(dest)
raise
else:
raiseOSError(err, $strerror(errno))
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [ExecIOEffect].} = tags: [ExecIOEffect].} =
## Executes a `shell command`:idx:. ## Executes a `shell command`:idx:.
@ -1616,7 +1640,8 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## returns the file size of `file`. Can raise ``OSError``. ## returns the file size of `file` (in bytes). An ``OSError`` exception is
## raised in case of an error.
when defined(windows): when defined(windows):
var a: WIN32_FIND_DATA var a: WIN32_FIND_DATA
var resA = findFirstFile(file, a) var resA = findFirstFile(file, a)

View file

@ -13,10 +13,6 @@
## ##
## Tested on these OSes: Linux, Windows, OSX ## Tested on these OSes: Linux, Windows, OSX
type
NilAccessError* = object of SystemError ## \
## Raised on dereferences of ``nil`` pointers.
# do allocate memory upfront: # do allocate memory upfront:
var se: ref NilAccessError var se: ref NilAccessError
new(se) new(se)

View file

@ -597,9 +597,12 @@ elif defined(JS):
result.month = Month(t.getMonth()) result.month = Month(t.getMonth())
result.year = t.getFullYear() result.year = t.getFullYear()
result.weekday = weekDays[t.getDay()] result.weekday = weekDays[t.getDay()]
result.yearday = 0
result.timezone = getTimezone() result.timezone = getTimezone()
result.yearday = result.monthday - 1
for month in mJan..<result.month:
result.yearday += getDaysInMonth(month, result.year)
proc getGMTime(t: Time): TimeInfo = proc getGMTime(t: Time): TimeInfo =
result.second = t.getUTCSeconds() result.second = t.getUTCSeconds()
result.minute = t.getUTCMinutes() result.minute = t.getUTCMinutes()
@ -608,7 +611,10 @@ elif defined(JS):
result.month = Month(t.getUTCMonth()) result.month = Month(t.getUTCMonth())
result.year = t.getUTCFullYear() result.year = t.getUTCFullYear()
result.weekday = weekDays[t.getUTCDay()] result.weekday = weekDays[t.getUTCDay()]
result.yearday = 0
result.yearday = result.monthday - 1
for month in mJan..<result.month:
result.yearday += getDaysInMonth(month, result.year)
proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo) proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)

View file

@ -375,7 +375,6 @@ template test*(name, body) {.dirty.} =
ensureFormattersInitialized() ensureFormattersInitialized()
if shouldRun(name): if shouldRun(name):
var stackTrace {.inject.}: string
checkpoints = @[] checkpoints = @[]
var testStatusIMPL {.inject.} = OK var testStatusIMPL {.inject.} = OK
@ -391,7 +390,7 @@ template test*(name, body) {.dirty.} =
except: except:
when not defined(js): when not defined(js):
checkpoint("Unhandled exception: " & getCurrentExceptionMsg()) checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
stackTrace = getCurrentException().getStackTrace() var stackTrace {.inject.} = getCurrentException().getStackTrace()
fail() fail()
finally: finally:

View file

@ -154,9 +154,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
discard discard
proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
## Builtin 'addr' operator for taking the address of a memory location. ## Builtin 'addr' operator for taking the address of a memory
## This works even for ``let`` variables or parameters for better interop ## location. This works even for ``let`` variables or parameters
## with C and so it is considered even more unsafe than the ordinary ``addr``. ## for better interop with C and so it is considered even more
## unsafe than the ordinary ``addr``. When you use it to write a
## wrapper for a C library, you should always check that the
## original library does never write to data behind the pointer that
## is returned from this procedure.
## Cannot be overloaded. ## Cannot be overloaded.
discard discard
@ -554,6 +558,10 @@ type
## Raised if it is attempted to send a message to a dead thread. ## Raised if it is attempted to send a message to a dead thread.
## ##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
NilAccessError* = object of SystemError ## \
## Raised on dereferences of ``nil`` pointers.
##
## This is only raised if the ``segfaults.nim`` module was imported!
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,

View file

@ -763,6 +763,8 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
template instantiateForRegion(allocator: untyped) = template instantiateForRegion(allocator: untyped) =
{.push stackTrace: off.}
when defined(fulldebug): when defined(fulldebug):
proc interiorAllocatedPtr*(p: pointer): pointer = proc interiorAllocatedPtr*(p: pointer): pointer =
result = interiorAllocatedPtr(allocator, p) result = interiorAllocatedPtr(allocator, p)
@ -850,5 +852,6 @@ template instantiateForRegion(allocator: untyped) =
proc getOccupiedSharedMem(): int = proc getOccupiedSharedMem(): int =
sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem) sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
{.pop.}
{.pop.} {.pop.}

View file

@ -50,7 +50,11 @@ proc chckRangeF(x, a, b: float): float =
proc chckNil(p: pointer) = proc chckNil(p: pointer) =
if p == nil: if p == nil:
sysFatal(ValueError, "attempt to write to a nil address") sysFatal(NilAccessError, "attempt to write to a nil address")
proc chckNilDisp(p: pointer) {.compilerproc.} =
if p == nil:
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
proc chckObj(obj, subclass: PNimType) {.compilerproc.} = proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
# checks if obj is of type subclass: # checks if obj is of type subclass:

View file

@ -642,6 +642,10 @@ proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} =
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
proc chckNilDisp(p: pointer) {.compilerproc.} =
if p == nil:
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
type NimString = string # hack for hti.nim type NimString = string # hack for hti.nim
include "system/hti" include "system/hti"

View file

@ -242,7 +242,6 @@ type
PGcThread = ptr GcThread PGcThread = ptr GcThread
GcThread {.pure, inheritable.} = object GcThread {.pure, inheritable.} = object
sys: SysThread
when emulatedThreadVars and not useStackMaskHack: when emulatedThreadVars and not useStackMaskHack:
tls: ThreadLocalStorage tls: ThreadLocalStorage
else: else:
@ -345,18 +344,16 @@ when not defined(useNimRtl):
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
type type
Thread* {.pure, final.}[TArg] = Thread* {.pure, final.}[TArg] = object
object of GcThread ## Nim thread. A thread is a heavy object (~14K) core: PGcThread
## that **must not** be part of a message! Use sys: SysThread
## a ``ThreadId`` for that.
when TArg is void: when TArg is void:
dataFn: proc () {.nimcall, gcsafe.} dataFn: proc () {.nimcall, gcsafe.}
else: else:
dataFn: proc (m: TArg) {.nimcall, gcsafe.} dataFn: proc (m: TArg) {.nimcall, gcsafe.}
data: TArg data: TArg
ThreadId*[TArg] = ptr Thread[TArg] ## the current implementation uses
## a pointer as a thread ID. {.deprecated: [TThread: Thread].}
{.deprecated: [TThread: Thread, TThreadId: ThreadId].}
var var
threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}] threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}]
@ -423,19 +420,20 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
when declared(threadType): when declared(threadType):
threadType = ThreadType.NimThread threadType = ThreadType.NimThread
when declared(registerThread): when declared(registerThread):
thrd.stackBottom = addr(thrd) thrd.core.stackBottom = addr(thrd)
registerThread(thrd) registerThread(thrd.core)
p(thrd) p(thrd)
when declared(registerThread): unregisterThread(thrd) when declared(registerThread): unregisterThread(thrd.core)
when declared(deallocOsPages): deallocOsPages() when declared(deallocOsPages): deallocOsPages()
else: else:
threadProcWrapDispatch(thrd) threadProcWrapDispatch(thrd)
template threadProcWrapperBody(closure: expr) {.immediate.} = template threadProcWrapperBody(closure: expr) {.immediate.} =
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) var thrd = cast[ptr Thread[TArg]](closure)
var core = thrd.core
when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
when declared(initAllocator): when declared(initAllocator):
initAllocator() initAllocator()
var thrd = cast[ptr Thread[TArg]](closure)
threadProcWrapStackFrame(thrd) threadProcWrapStackFrame(thrd)
# Since an unhandled exception terminates the whole process (!), there is # Since an unhandled exception terminates the whole process (!), there is
# no need for a ``try finally`` here, nor would it be correct: The current # no need for a ``try finally`` here, nor would it be correct: The current
@ -444,7 +442,9 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
# page! # page!
# mark as not running anymore: # mark as not running anymore:
thrd.core = nil
thrd.dataFn = nil thrd.dataFn = nil
deallocShared(cast[pointer](core))
{.push stack_trace:off.} {.push stack_trace:off.}
when defined(windows): when defined(windows):
@ -502,6 +502,10 @@ when false:
discard pthread_cancel(t.sys) discard pthread_cancel(t.sys)
when declared(registerThread): unregisterThread(addr(t)) when declared(registerThread): unregisterThread(addr(t))
t.dataFn = nil t.dataFn = nil
## if thread `t` already exited, `t.core` will be `null`.
if not isNil(t.core):
deallocShared(t.core)
t.core = nil
when hostOS == "windows": when hostOS == "windows":
proc createThread*[TArg](t: var Thread[TArg], proc createThread*[TArg](t: var Thread[TArg],
@ -510,9 +514,11 @@ when hostOS == "windows":
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
## don't need to pass any data to the thread. ## don't need to pass any data to the thread.
t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
when TArg isnot void: t.data = param when TArg isnot void: t.data = param
t.dataFn = tp t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize when hasSharedHeap: t.core.stackSize = ThreadStackSize
var dummyThreadId: int32 var dummyThreadId: int32
t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
addr(t), 0'i32, dummyThreadId) addr(t), 0'i32, dummyThreadId)
@ -532,9 +538,11 @@ else:
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
## don't need to pass any data to the thread. ## don't need to pass any data to the thread.
t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
when TArg isnot void: t.data = param when TArg isnot void: t.data = param
t.dataFn = tp t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize when hasSharedHeap: t.core.stackSize = ThreadStackSize
var a {.noinit.}: PthreadAttr var a {.noinit.}: PthreadAttr
pthread_attr_init(a) pthread_attr_init(a)
pthread_attr_setstacksize(a, ThreadStackSize) pthread_attr_setstacksize(a, ThreadStackSize)
@ -554,10 +562,6 @@ else:
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
createThread[void](t, tp) createThread[void](t, tp)
proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} =
## returns the thread ID of `t`.
result = addr(t)
when false: when false:
proc mainThreadId*[TArg](): ThreadId[TArg] = proc mainThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the main thread. ## returns the thread ID of the main thread.

View file

@ -667,6 +667,8 @@ const
# Error Constants # Error Constants
const const
ERROR_FILE_NOT_FOUND* = 2
ERROR_PATH_NOT_FOUND* = 3
ERROR_ACCESS_DENIED* = 5 ERROR_ACCESS_DENIED* = 5
ERROR_HANDLE_EOF* = 38 ERROR_HANDLE_EOF* = 38
ERROR_BAD_ARGUMENTS* = 165 ERROR_BAD_ARGUMENTS* = 165

View file

@ -1,7 +1,5 @@
/* linenoise.c -- VERSION 1.0 /* linenoise.c -- guerrilla line editing library against the idea that a
* * line editing lib needs to be 20,000 lines of C code.
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
* *
* You can find the latest source code at: * You can find the latest source code at:
* *
@ -12,7 +10,7 @@
* *
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* *
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
@ -113,17 +111,18 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#ifndef __LINENOISE_H #include "linenoise.h"
# include "clinenoise.h"
#endif
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
#define LINENOISE_MAX_LINE 4096 #define LINENOISE_MAX_LINE 4096
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
static linenoiseCompletionCallback *completionCallback = NULL; static linenoiseCompletionCallback *completionCallback = NULL;
static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
static struct termios orig_termios; /* In order to restore at exit.*/ static struct termios orig_termios; /* In order to restore at exit.*/
static int rawmode = 0; /* For atexit() function to check if restore is needed*/ static int rawmode = 0; /* For atexit() function to check if restore is needed*/
@ -141,7 +140,7 @@ struct linenoiseState {
int ofd; /* Terminal stdout file descriptor. */ int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */ char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */ size_t buflen; /* Edited line buffer size. */
char *prompt; /* Prompt to display. */ const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */ size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */ size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */ size_t oldpos; /* Previous refresh cursor position. */
@ -174,7 +173,7 @@ enum KEY_ACTION{
}; };
static void linenoiseAtExit(void); static void linenoiseAtExit(void);
int linenoiseHistoryAdd(char *line); int linenoiseHistoryAdd(const char *line);
static void refreshLine(struct linenoiseState *l); static void refreshLine(struct linenoiseState *l);
/* Debugging macro. */ /* Debugging macro. */
@ -411,18 +410,30 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn; completionCallback = fn;
} }
/* Register a hits function to be called to show hits to the user at the
* right of the prompt. */
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
hintsCallback = fn;
}
/* Register a function to free the hints returned by the hints callback
* registered with linenoiseSetHintsCallback(). */
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
freeHintsCallback = fn;
}
/* This function is used by the callback function registered by the user /* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the * in order to add completion options given the input string when the
* user typed <tab>. See the example.c source code for a very easy to * user typed <tab>. See the example.c source code for a very easy to
* understand example. */ * understand example. */
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) { void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
size_t len = strlen(str); size_t len = strlen(str);
char *copy, **cvec; char *copy, **cvec;
copy = (char*)malloc(len+1); copy = malloc(len+1);
if (copy == NULL) return; if (copy == NULL) return;
memcpy(copy,str,len+1); memcpy(copy,str,len+1);
cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1)); cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
if (cvec == NULL) { if (cvec == NULL) {
free(copy); free(copy);
return; return;
@ -447,12 +458,12 @@ static void abInit(struct abuf *ab) {
ab->len = 0; ab->len = 0;
} }
static void abAppend(struct abuf *ab, char *s, int len) { static void abAppend(struct abuf *ab, const char *s, int len) {
char *neww = (char*)realloc(ab->b,ab->len+len); char *new = realloc(ab->b,ab->len+len);
if (neww == NULL) return; if (new == NULL) return;
memcpy(neww+ab->len,s,len); memcpy(new+ab->len,s,len);
ab->b = neww; ab->b = new;
ab->len += len; ab->len += len;
} }
@ -460,6 +471,30 @@ static void abFree(struct abuf *ab) {
free(ab->b); free(ab->b);
} }
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
* to the right of the prompt. */
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
char seq[64];
if (hintsCallback && plen+l->len < l->cols) {
int color = -1, bold = 0;
char *hint = hintsCallback(l->buf,&color,&bold);
if (hint) {
int hintlen = strlen(hint);
int hintmaxlen = l->cols-(plen+l->len);
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
if (bold == 1 && color == -1) color = 37;
if (color != -1 || bold != 0)
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
abAppend(ab,seq,strlen(seq));
abAppend(ab,hint,hintlen);
if (color != -1 || bold != 0)
abAppend(ab,"\033[0m",4);
/* Call the function to free the hint returned. */
if (freeHintsCallback) freeHintsCallback(hint);
}
}
}
/* Single line low level line refresh. /* Single line low level line refresh.
* *
* Rewrite the currently edited line accordingly to the buffer content, * Rewrite the currently edited line accordingly to the buffer content,
@ -489,6 +524,8 @@ static void refreshSingleLine(struct linenoiseState *l) {
/* Write the prompt and the current buffer content */ /* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->prompt,strlen(l->prompt));
abAppend(&ab,buf,len); abAppend(&ab,buf,len);
/* Show hits if any. */
refreshShowHints(&ab,l,plen);
/* Erase to right */ /* Erase to right */
snprintf(seq,64,"\x1b[0K"); snprintf(seq,64,"\x1b[0K");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
@ -542,6 +579,9 @@ static void refreshMultiLine(struct linenoiseState *l) {
abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->prompt,strlen(l->prompt));
abAppend(&ab,l->buf,l->len); abAppend(&ab,l->buf,l->len);
/* Show hits if any. */
refreshShowHints(&ab,l,plen);
/* If we are at the very end of the screen with our prompt, we need to /* If we are at the very end of the screen with our prompt, we need to
* emit a newline and move the prompt to the first column. */ * emit a newline and move the prompt to the first column. */
if (l->pos && if (l->pos &&
@ -602,7 +642,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
l->pos++; l->pos++;
l->len++; l->len++;
l->buf[l->len] = '\0'; l->buf[l->len] = '\0';
if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
/* Avoid a full update of the line in the /* Avoid a full update of the line in the
* trivial case. */ * trivial case. */
if (write(l->ofd,&c,1) == -1) return -1; if (write(l->ofd,&c,1) == -1) return -1;
@ -725,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
* when ctrl+d is typed. * when ctrl+d is typed.
* *
* The function returns the length of the current buffer. */ * The function returns the length of the current buffer. */
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt) static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
{ {
struct linenoiseState l; struct linenoiseState l;
@ -776,6 +816,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
history_len--; history_len--;
free(history[history_len]); free(history[history_len]);
if (mlmode) linenoiseEditMoveEnd(&l); if (mlmode) linenoiseEditMoveEnd(&l);
if (hintsCallback) {
/* Force a refresh without hints to leave the previous
* line as the user typed it after a newline. */
linenoiseHintsCallback *hc = hintsCallback;
hintsCallback = NULL;
refreshLine(&l);
hintsCallback = hc;
}
return (int)l.len; return (int)l.len;
case CTRL_C: /* ctrl-c */ case CTRL_C: /* ctrl-c */
errno = EAGAIN; errno = EAGAIN;
@ -931,41 +979,71 @@ void linenoisePrintKeyCodes(void) {
/* This function calls the line editing function linenoiseEdit() using /* This function calls the line editing function linenoiseEdit() using
* the STDIN file descriptor set in raw mode. */ * the STDIN file descriptor set in raw mode. */
static int linenoiseRaw(char *buf, size_t buflen, char *prompt) { static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
int count; int count;
if (buflen == 0) { if (buflen == 0) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
if (!isatty(STDIN_FILENO)) {
/* Not a tty: read from file / pipe. */
if (fgets(buf, buflen, stdin) == NULL) return -1;
count = strlen(buf);
if (count && buf[count-1] == '\n') {
count--;
buf[count] = '\0';
}
} else {
/* Interactive editing. */
if (enableRawMode(STDIN_FILENO) == -1) return -1; if (enableRawMode(STDIN_FILENO) == -1) return -1;
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
disableRawMode(STDIN_FILENO); disableRawMode(STDIN_FILENO);
printf("\n"); printf("\n");
}
return count; return count;
} }
/* This function is called when linenoise() is called with the standard
* input file descriptor not attached to a TTY. So for example when the
* program using linenoise is called in pipe or with a file redirected
* to its standard input. In this case, we want to be able to return the
* line regardless of its length (by default we are limited to 4k). */
static char *linenoiseNoTTY(void) {
char *line = NULL;
size_t len = 0, maxlen = 0;
while(1) {
if (len == maxlen) {
if (maxlen == 0) maxlen = 16;
maxlen *= 2;
char *oldval = line;
line = realloc(line,maxlen);
if (line == NULL) {
if (oldval) free(oldval);
return NULL;
}
}
int c = fgetc(stdin);
if (c == EOF || c == '\n') {
if (c == EOF && len == 0) {
free(line);
return NULL;
} else {
line[len] = '\0';
return line;
}
} else {
line[len] = c;
len++;
}
}
}
/* The high level function that is the main API of the linenoise library. /* The high level function that is the main API of the linenoise library.
* This function checks if the terminal has basic capabilities, just checking * This function checks if the terminal has basic capabilities, just checking
* for a blacklist of stupid terminals, and later either calls the line * for a blacklist of stupid terminals, and later either calls the line
* editing function or uses dummy fgets() so that you will be able to type * editing function or uses dummy fgets() so that you will be able to type
* something even in the most desperate of the conditions. */ * something even in the most desperate of the conditions. */
char *linenoise(char *prompt) { char *linenoise(const char *prompt) {
char buf[LINENOISE_MAX_LINE]; char buf[LINENOISE_MAX_LINE];
int count; int count;
if (isUnsupportedTerm()) { if (!isatty(STDIN_FILENO)) {
/* Not a tty: read from file / pipe. In this mode we don't want any
* limit to the line size, so we call a function to handle that. */
return linenoiseNoTTY();
} else if (isUnsupportedTerm()) {
size_t len; size_t len;
printf("%s",prompt); printf("%s",prompt);
@ -984,6 +1062,14 @@ char *linenoise(char *prompt) {
} }
} }
/* This is just a wrapper the user may want to call in order to make sure
* the linenoise returned buffer is freed with the same allocator it was
* created with. Useful when the main program is using an alternative
* allocator. */
void linenoiseFree(void *ptr) {
free(ptr);
}
/* ================================ History ================================= */ /* ================================ History ================================= */
/* Free the history, but does not reset it. Only used when we have to /* Free the history, but does not reset it. Only used when we have to
@ -1011,14 +1097,14 @@ static void linenoiseAtExit(void) {
* histories, but will work well for a few hundred of entries. * histories, but will work well for a few hundred of entries.
* *
* Using a circular buffer is smarter, but a bit more complex to handle. */ * Using a circular buffer is smarter, but a bit more complex to handle. */
int linenoiseHistoryAdd(char *line) { int linenoiseHistoryAdd(const char *line) {
char *linecopy; char *linecopy;
if (history_max_len == 0) return 0; if (history_max_len == 0) return 0;
/* Initialization on first call. */ /* Initialization on first call. */
if (history == NULL) { if (history == NULL) {
history = (char**)malloc(sizeof(char*)*history_max_len); history = malloc(sizeof(char*)*history_max_len);
if (history == NULL) return 0; if (history == NULL) return 0;
memset(history,0,(sizeof(char*)*history_max_len)); memset(history,0,(sizeof(char*)*history_max_len));
} }
@ -1045,14 +1131,14 @@ int linenoiseHistoryAdd(char *line) {
* just the latest 'len' elements if the new history length value is smaller * just the latest 'len' elements if the new history length value is smaller
* than the amount of items already inside the history. */ * than the amount of items already inside the history. */
int linenoiseHistorySetMaxLen(int len) { int linenoiseHistorySetMaxLen(int len) {
char **neww; char **new;
if (len < 1) return 0; if (len < 1) return 0;
if (history) { if (history) {
int tocopy = history_len; int tocopy = history_len;
neww = (char**)malloc(sizeof(char*)*len); new = malloc(sizeof(char*)*len);
if (neww == NULL) return 0; if (new == NULL) return 0;
/* If we can't copy everything, free the elements we'll not use. */ /* If we can't copy everything, free the elements we'll not use. */
if (len < tocopy) { if (len < tocopy) {
@ -1061,10 +1147,10 @@ int linenoiseHistorySetMaxLen(int len) {
for (j = 0; j < tocopy-len; j++) free(history[j]); for (j = 0; j < tocopy-len; j++) free(history[j]);
tocopy = len; tocopy = len;
} }
memset(neww,0,sizeof(char*)*len); memset(new,0,sizeof(char*)*len);
memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy); memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
free(history); free(history);
history = neww; history = new;
} }
history_max_len = len; history_max_len = len;
if (history_len > history_max_len) if (history_len > history_max_len)
@ -1074,11 +1160,15 @@ int linenoiseHistorySetMaxLen(int len) {
/* Save the history in the specified file. On success 0 is returned /* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */ * otherwise -1 is returned. */
int linenoiseHistorySave(char *filename) { int linenoiseHistorySave(const char *filename) {
FILE *fp = fopen(filename,"w"); mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
FILE *fp;
int j; int j;
fp = fopen(filename,"w");
umask(old_umask);
if (fp == NULL) return -1; if (fp == NULL) return -1;
chmod(filename,S_IRUSR|S_IWUSR);
for (j = 0; j < history_len; j++) for (j = 0; j < history_len; j++)
fprintf(fp,"%s\n",history[j]); fprintf(fp,"%s\n",history[j]);
fclose(fp); fclose(fp);
@ -1090,7 +1180,7 @@ int linenoiseHistorySave(char *filename) {
* *
* If the file exists and the operation succeeded 0 is returned, otherwise * If the file exists and the operation succeeded 0 is returned, otherwise
* on error -1 is returned. */ * on error -1 is returned. */
int linenoiseHistoryLoad(char *filename) { int linenoiseHistoryLoad(const char *filename) {
FILE *fp = fopen(filename,"r"); FILE *fp = fopen(filename,"r");
char buf[LINENOISE_MAX_LINE]; char buf[LINENOISE_MAX_LINE];

View file

@ -39,22 +39,35 @@
#ifndef __LINENOISE_H #ifndef __LINENOISE_H
#define __LINENOISE_H #define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct linenoiseCompletions { typedef struct linenoiseCompletions {
size_t len; size_t len;
char **cvec; char **cvec;
} linenoiseCompletions; } linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *); typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, char *); void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
char *linenoise(char *prompt); char *linenoise(const char *prompt);
int linenoiseHistoryAdd(char *line); void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(char *filename); int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(char *filename); int linenoiseHistoryLoad(const char *filename);
void linenoiseClearScreen(void); void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml); void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void); void linenoisePrintKeyCodes(void);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */ #endif /* __LINENOISE_H */

View file

@ -14,8 +14,7 @@ type
CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.} CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
{.emit: staticRead"clinenoise.h".} {.compile: "linenoise.c".}
{.emit: staticRead"clinenoise.c".}
proc setCompletionCallback*(a2: ptr CompletionCallback) {. proc setCompletionCallback*(a2: ptr CompletionCallback) {.
importc: "linenoiseSetCompletionCallback".} importc: "linenoiseSetCompletionCallback".}

View file

@ -485,6 +485,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
incl gGlobalOptions, optCaasEnabled incl gGlobalOptions, optCaasEnabled
isServing = true isServing = true
wantMainModule() wantMainModule()
if not fileExists(gProjectFull):
quit "cannot find file: " & gProjectFull
add(searchPaths, options.libpath) add(searchPaths, options.libpath)
# do not stop after the first error: # do not stop after the first error:

View file

@ -0,0 +1,17 @@
discard """
output: '''true'''
"""
import deques
proc index(self: Deque[int], idx: Natural): int =
self[idx]
proc main =
var testDeque = initDeque[int]()
testDeque.addFirst(1)
assert testDeque.index(0) == 1
main()
echo "true"

View file

@ -1,10 +1,10 @@
discard """ discard """
line: 10 line: 10
errormsg: "value of type 'bool' has to be discarded" errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded'''
""" """
proc p = proc p =
var f: TFile var f: File
echo "hi" echo "hi"
open(f, "arg.txt") open(f, "arg.txt")

View file

@ -1,5 +1,5 @@
discard """ discard """
errormsg: "value of type 'string' has to be discarded" errormsg: '''expression '"invalid"' is of type 'string' and has to be discarded'''
line: 12 line: 12
""" """

View file

@ -1,5 +1,5 @@
discard """ discard """
errormsg: "value of type 'bool' has to be discarded" errormsg: "expression 'true' is of type 'bool' and has to be discarded"
line: 13 line: 13
file: "tdont_show_system.nim" file: "tdont_show_system.nim"
""" """

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 10 line: 10
errormsg: "value of type 'string' has to be discarded" errormsg: "expression 'result[1 .. -(len(result), 1)]' is of type 'string' and has to be discarded"
""" """
# bug #578 # bug #578

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tstmtexp.nim" file: "tstmtexp.nim"
line: 8 line: 8
errormsg: "value of type 'int literal(5)' has to be discarded" errormsg: "expression '5' is of type 'int literal(5)' and has to be discarded"
""" """
# Test 3 # Test 3

View file

@ -14,3 +14,16 @@ proc arrayItem(a: ArrayType): auto =
var arr: ArrayType[int] var arr: ArrayType[int]
echo arrayItem(arr) echo arrayItem(arr)
# bug #5597
template fail() = "what"
proc g[T](x: var T) =
x.fail = 3
type
Obj = object
fail: int
var y: Obj
g y

View file

@ -1,5 +1,6 @@
discard """ discard """
output: '''b() output: '''b()
720 120.0
720 120.0''' 720 120.0'''
""" """
@ -16,8 +17,6 @@ proc fac[T](x: T): T =
echo fac(6), " ", fac(5.0) echo fac(6), " ", fac(5.0)
when false:
# This still doesn't work...
# test recursive generic with forwarding: # test recursive generic with forwarding:
proc fac2[T](x: T): T proc fac2[T](x: T): T
@ -26,3 +25,4 @@ when false:
proc fac2[T](x: T): T = proc fac2[T](x: T): T =
if x == 0: return 1 if x == 0: return 1
else: return fac2(x-1)*x else: return fac2(x-1)*x

View file

@ -1,7 +1,6 @@
discard """ discard """
output: "1.1000000000000001e+00 11" output: "1.1 11\n42\n0"
ccodecheck: "!@'ClEnv'" ccodecheck: "!@'ClEnv'"
disabled: "true"
""" """
proc p[T](a, b: T): T proc p[T](a, b: T): T
@ -12,3 +11,18 @@ proc p[T](a, b: T): T =
let c = b let c = b
result = a + b + c result = a + b + c
# https://github.com/nim-lang/Nim/issues/4908
proc foo(t: typedesc[int]): int
proc bar(): int = foo(int)
proc foo(t: typedesc[int]): int =
return 0
# https://github.com/nim-lang/Nim/issues/4104
proc print[T](t: T) # Error: implementation of 'print.print(t: int)' expected
print 42 # moving this line after the implementation fixes the error,
# but such behaviour makes forward declaration pointless
proc print[T](t: T) =
echo t
echo bar()

View file

@ -0,0 +1,65 @@
discard """
output: '''(peel: 0, color: 15)
(color: 15)
17
(width: 0.0, taste: nil, color: 13)
(width: 0.0, taste: nil, color: 15)
cool'''
"""
# bug #5241
type
BaseFruit[T] = object of RootObj
color: T
MidLevel[T] = object of BaseFruit[T]
Mango = object of MidLevel[int]
peel: int
Peach[X, T, Y] = object of T
width: X
taste: Y
proc setColor[T](self: var BaseFruit[T]) =
self.color = 15
proc setColor[T](self: var BaseFruit[T], c: int) =
self.color = c
var c: Mango
setColor(c)
echo c
var d: MidLevel[int]
setColor(d)
echo d
type
FooBase[T] = ref object of RootRef
v: T
BarClient = ref object of FooBase[int]
proc getColor[T](f: FooBase[T]): T = 17
var b: BarClient
echo getColor(b)
var z: Peach[float64, BaseFruit[int], string]
z.setColor(13)
echo z
z.setColor()
echo z
# bug #5411
type
Foo[T] = ref object of RootRef
v: T
Bar = ref object of Foo[int]
method m(o: RootRef) {.base.} = assert(false, "Abstract method called")
method m[T](o: Foo[T]) = echo "cool"
var v: Bar
v.new()
v.m() # Abstract method not called anymore

13
tests/js/ttimes.nim Normal file
View file

@ -0,0 +1,13 @@
# test times module with js
discard """
action: run
"""
import times
# $ date --date='@2147483647'
# Tue 19 Jan 03:14:07 GMT 2038
block yeardayTest:
# check if yearday attribute is properly set on TimeInfo creation
doAssert fromSeconds(2147483647).getGMTime().yearday == 18

View file

@ -35,5 +35,6 @@ except:
echo "Second readLine raised an exception" echo "Second readLine raised an exception"
echo line echo line
f.close()
removeFile(fn) removeFile(fn)

View file

@ -96,5 +96,12 @@ suite "bug #4494":
check: check:
allIt(0..3, tags[it] != tags[it + 1]) allIt(0..3, tags[it] != tags[it + 1])
suite "bug #5571":
test "can define gcsafe procs within tests":
proc doTest {.gcsafe.} =
let line = "a"
check: line == "a"
doTest()
static: static:
echo "compile end" echo "compile end"

View file

@ -186,25 +186,11 @@ proc longGCTests(r: var TResults, cat: Category, options: string) =
proc threadTests(r: var TResults, cat: Category, options: string) = proc threadTests(r: var TResults, cat: Category, options: string) =
template test(filename: untyped) = template test(filename: untyped) =
testSpec r, makeTest("tests/threads" / filename, options, cat, actionRun) testSpec r, makeTest(filename, options, cat, actionRun)
testSpec r, makeTest("tests/threads" / filename, options & testSpec r, makeTest(filename, options & " -d:release", cat, actionRun)
" -d:release", cat, actionRun) testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat, actionRun)
testSpec r, makeTest("tests/threads" / filename, options & for t in os.walkFiles("tests/threads/t*.nim"):
" --tlsEmulation:on", cat, actionRun) test(t)
test "tactors"
test "tactors2"
test "threadex"
# deactivated because output capturing still causes problems sometimes:
#test "trecursive_actor"
#test "threadring"
#test "tthreadanalysis"
#test "tthreadsort"
test "tthreadanalysis2"
#test "tthreadanalysis3"
test "tthreadheapviolation1"
test "tonthreadcreation"
test "tracy_allocator"
# ------------------------- IO tests ------------------------------------------ # ------------------------- IO tests ------------------------------------------

View file

@ -0,0 +1,30 @@
discard """
outputsub: "129"
"""
import os, locks
type
MarkerObj = object
lock: Lock
counter: int
Marker = ptr MarkerObj
const
ThreadsCount = 129
SleepTime = 1000
proc worker(p: Marker) {.thread.} =
acquire(p.lock)
inc(p.counter)
release(p.lock)
sleep(SleepTime)
var p = cast[Marker](allocShared0(sizeof(MarkerObj)))
initLock(p.lock)
var ts = newSeq[Thread[Marker]](ThreadsCount)
for i in 0..<ts.len:
createThread(ts[i], worker, p)
joinThreads(ts)
echo p.counter

View file

@ -1,4 +1,5 @@
discard """ discard """
disabled: yes
outputsub: "0" outputsub: "0"
""" """

View file

@ -0,0 +1,28 @@
discard """
outputsub: "129"
"""
import os, locks
type
MarkerObj = object
lock: Lock
counter: int
Marker = ptr MarkerObj
const
ThreadsCount = 129
proc worker(p: Marker) {.thread.} =
acquire(p.lock)
inc(p.counter)
release(p.lock)
var p = cast[Marker](allocShared0(sizeof(MarkerObj)))
initLock(p.lock)
for i in 0..(ThreadsCount - 1):
var thread: Thread[Marker]
createThread(thread, worker, p)
joinThread(thread)
echo p.counter

View file

@ -1,4 +1,5 @@
discard """ discard """
disabled: yes
outputsub: "101" outputsub: "101"
errormsg: "'threadFunc' is not GC-safe" errormsg: "'threadFunc' is not GC-safe"
line: 39 line: 39

View file

@ -1,4 +1,5 @@
discard """ discard """
disabled: yes
outputsub: "channel is empty" outputsub: "channel is empty"
""" """

View file

@ -52,13 +52,13 @@ test(OrderR, OrderG)
test(OrderR, OrderAlias) test(OrderR, OrderAlias)
test(OrderG, OrderAlias) test(OrderG, OrderAlias)
typeRep(OrderAlias, Order) # true typeRep(OrderAlias.R, Order.R) # true
typeRep(OrderR, Order) # true typeRep(OrderR.R, Order.R) # true
typeRep(OrderG, Order) # true typeRep(OrderG.R, Order.R) # true
typeRep(OrderR, OrderAlias) # true typeRep(OrderR.R, OrderAlias.R) # true
typeRep(OrderG, OrderAlias) # true typeRep(OrderG.R, OrderAlias.R) # true
typeRep(OrderR, OrderG) # true typeRep(OrderR.R, OrderG.R) # true
echo OrderR.R # R echo OrderR.R # R
echo OrderG.R # R echo OrderG.R # R

View file

@ -42,6 +42,9 @@ Changes affecting backwards compatibility
AST that is the same as what is used to define an enum. Previously the AST AST that is the same as what is used to define an enum. Previously the AST
returned had a repeated ``EnumTy`` node and was missing the initial pragma returned had a repeated ``EnumTy`` node and was missing the initial pragma
node (which is currently empty for an enum). node (which is currently empty for an enum).
- If the dispatcher parameter's value used in multi method is ``nil``,
a ``NilError`` exception is raised. The old behavior was that the method
would be a ``nop`` then.
Library Additions Library Additions