Merge branch 'devel' into araq-devel

This commit is contained in:
Andreas Rumpf 2018-07-01 16:24:56 +02:00
commit e455023902
18 changed files with 175 additions and 64 deletions

View file

@ -1587,20 +1587,24 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
e.sons[2] e.sons[2]
initLocExpr(p, ea, a) initLocExpr(p, ea, a)
initLoc(b, locExpr, e, OnUnknown) initLoc(b, locExpr, e, OnUnknown)
b.r = rope("(")
var length = sonsLen(e.sons[1]) var length = sonsLen(e.sons[1])
for i in countup(0, length - 1): if length > 0:
let it = e.sons[1].sons[i] b.r = rope("(")
if it.kind == nkRange: for i in countup(0, length - 1):
initLocExpr(p, it.sons[0], x) let it = e.sons[1].sons[i]
initLocExpr(p, it.sons[1], y) if it.kind == nkRange:
addf(b.r, "$1 >= $2 && $1 <= $3", initLocExpr(p, it.sons[0], x)
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) initLocExpr(p, it.sons[1], y)
else: addf(b.r, "$1 >= $2 && $1 <= $3",
initLocExpr(p, it, x) [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) else:
if i < length - 1: add(b.r, " || ") initLocExpr(p, it, x)
add(b.r, ")") addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < length - 1: add(b.r, " || ")
add(b.r, ")")
else:
# handle the case of an empty set
b.r = rope("0")
putIntoDest(p, d, e, b.r) putIntoDest(p, d, e, b.r)
else: else:
assert(e.sons[1].typ != nil) assert(e.sons[1].typ != nil)

View file

@ -344,7 +344,8 @@ proc getNumber(L: var TLexer, result: var TToken) =
if buf[pos] == '_': if buf[pos] == '_':
if buf[pos+1] notin chars: if buf[pos+1] notin chars:
lexMessage(L, errGenerated, lexMessage(L, errGenerated,
"only single underscores may occur in a token: '__' is invalid") "only single underscores may occur in a token and token may not " &
"end with an underscore: e.g. '1__1' and '1_' are invalid")
break break
add(tok.literal, '_') add(tok.literal, '_')
inc(pos) inc(pos)

View file

@ -76,7 +76,7 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
result = arg result = arg
let x = result.skipConv let x = result.skipConv
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyExpr:
changeType(c, x, formal, check=true) changeType(c, x, formal, check=true)
else: else:
result = skipHiddenSubConv(result) result = skipHiddenSubConv(result)

View file

@ -393,6 +393,8 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
# When the right-hand side is an explicit type, we must # When the right-hand side is an explicit type, we must
# not allow regular values to be matched against the type: # not allow regular values to be matched against the type:
liftLhs = false liftLhs = false
else:
n.sons[2] = semExpr(c, n[2])
var lhsType = n[1].typ var lhsType = n[1].typ
if lhsType.kind != tyTypeDesc: if lhsType.kind != tyTypeDesc:
@ -906,19 +908,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
proc semExprNoType(c: PContext, n: PNode): PNode = proc semExprNoType(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {efWantStmt}) result = semExpr(c, n, {efWantStmt})
# make an 'if' expression an 'if' statement again for backwards
# compatibility (.discardable was a bad idea!); bug #6980
var isStmt = false
if result.kind == nkIfExpr:
isStmt = true
for condActionPair in result:
let action = condActionPair.lastSon
if not implicitlyDiscardable(action) and not
endsInNoReturn(action):
isStmt = false
if isStmt:
result.kind = nkIfStmt
result.typ = nil
discardCheck(c, result) discardCheck(c, result)
proc isTypeExpr(n: PNode): bool = proc isTypeExpr(n: PNode): bool =

View file

@ -54,7 +54,8 @@ 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) and not fromDotExpr sfCustomPragma notin s.flags and s.kind in {skMacro, skTemplate} and
(s.typ.len == 1) and not fromDotExpr
template isMixedIn(sym): bool = template isMixedIn(sym): bool =
let s = sym let s = sym

View file

@ -111,13 +111,15 @@ proc semExprBranchScope(c: PContext, n: PNode): PNode =
const const
skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch, skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch, nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr} nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
nkHiddenStdConv}
proc implicitlyDiscardable(n: PNode): bool = proc implicitlyDiscardable(n: PNode): bool =
var n = n var n = n
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
result = isCallExpr(n) and n.sons[0].kind == nkSym and result = n.kind == nkRaiseStmt or
sfDiscardable in n.sons[0].sym.flags (isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags)
proc fixNilType(c: PContext; n: PNode) = proc fixNilType(c: PContext; n: PNode) =
if isAtom(n): if isAtom(n):
@ -132,11 +134,8 @@ proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if implicitlyDiscardable(result): if implicitlyDiscardable(result):
var n = result var n = newNodeI(nkDiscardStmt, result.info, 1)
result.typ = nil n[0] = result
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon

View file

@ -623,7 +623,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred, tyUserTypeClassInst: tyStatic, tyInferred, tyUserTypeClasses:
result = firstOrd(conf, lastSon(t)) result = firstOrd(conf, lastSon(t))
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = firstOrd(conf, lastSon(t)) if t.len > 0: result = firstOrd(conf, lastSon(t))
@ -642,7 +642,7 @@ proc firstFloat*(t: PType): BiggestFloat =
getFloatValue(t.n.sons[0]) getFloatValue(t.n.sons[0])
of tyVar: firstFloat(t.sons[0]) of tyVar: firstFloat(t.sons[0])
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred: tyStatic, tyInferred, tyUserTypeClasses:
firstFloat(lastSon(t)) firstFloat(lastSon(t))
else: else:
internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
@ -679,7 +679,7 @@ proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred: tyStatic, tyInferred, tyUserTypeClasses:
result = lastOrd(conf, lastSon(t)) result = lastOrd(conf, lastSon(t))
of tyProxy: result = 0 of tyProxy: result = 0
of tyOrdinal: of tyOrdinal:
@ -699,7 +699,7 @@ proc lastFloat*(t: PType): BiggestFloat =
assert(t.n.kind == nkRange) assert(t.n.kind == nkRange)
getFloatValue(t.n.sons[1]) getFloatValue(t.n.sons[1])
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred: tyStatic, tyInferred, tyUserTypeClasses:
lastFloat(lastSon(t)) lastFloat(lastSon(t))
else: else:
internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
@ -707,7 +707,7 @@ proc lastFloat*(t: PType): BiggestFloat =
proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt = proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
case t.kind case t.skipTypes(tyUserTypeClasses).kind
of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t) of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
of tyDistinct: result = lengthOrd(conf, t.sons[0]) of tyDistinct: result = lengthOrd(conf, t.sons[0])
else: else:
@ -717,7 +717,7 @@ proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
if last == high(BiggestInt) and first <= 0: if last == high(BiggestInt) and first <= 0:
result = last result = last
else: else:
result = lastOrd(conf, t) - firstOrd(conf, t) + 1 result = last - first + 1
# -------------- type equality ----------------------------------------------- # -------------- type equality -----------------------------------------------

View file

@ -209,7 +209,7 @@ proc writeField(n: var PNode, x: TFullReg) =
of rkNone: discard of rkNone: discard
of rkInt: n.intVal = x.intVal of rkInt: n.intVal = x.intVal
of rkFloat: n.floatVal = x.floatVal of rkFloat: n.floatVal = x.floatVal
of rkNode: n = x.node of rkNode: n = copyValue(x.node)
of rkRegisterAddr: writeField(n, x.regAddr[]) of rkRegisterAddr: writeField(n, x.regAddr[])
of rkNodeAddr: n = x.nodeAddr[] of rkNodeAddr: n = x.nodeAddr[]
@ -622,6 +622,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
stackTrace(c, tos, pc, errNilAccess) stackTrace(c, tos, pc, errNilAccess)
regs[ra].nodeAddr[][] = n[] regs[ra].nodeAddr[][] = n[]
regs[ra].nodeAddr[].flags.incl nfIsRef regs[ra].nodeAddr[].flags.incl nfIsRef
# `var object` parameters are sent as rkNodeAddr. When they are mutated
# vmgen generates opcWrDeref, which means that we must dereference
# twice.
# TODO: This should likely be handled differently in vmgen.
elif (nfIsRef notin regs[ra].nodeAddr[].flags and
nfIsRef notin n.flags):
regs[ra].nodeAddr[][] = n[]
else: else:
regs[ra].nodeAddr[] = n regs[ra].nodeAddr[] = n
of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
@ -912,6 +919,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if a.kind == nkSym: if a.kind == nkSym:
regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
else: copyTree(a.sym.ast) else: copyTree(a.sym.ast)
regs[ra].node.flags.incl nfIsRef
else: else:
stackTrace(c, tos, pc, "node is not a symbol") stackTrace(c, tos, pc, "node is not a symbol")
of opcEcho: of opcEcho:
@ -1210,8 +1218,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcIsNil: of opcIsNil:
decodeB(rkInt) decodeB(rkInt)
let node = regs[rb].node let node = regs[rb].node
regs[ra].intVal = ord(node.kind == nkNilLit or regs[ra].intVal = ord(
(node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil)) # Note that `nfIsRef` + `nkNilLit` represents an allocated
# reference with the value `nil`, so `isNil` should be false!
(node.kind == nkNilLit and nfIsRef notin node.flags) or
(node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil) or
(not node.typ.isNil and node.typ.kind == tyProc and
node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
node.sons[1].kind == nkNilLit))
of opcNBindSym: of opcNBindSym:
decodeBx(rkNode) decodeBx(rkNode)
regs[ra].node = copyTree(c.constants.sons[rbx]) regs[ra].node = copyTree(c.constants.sons[rbx])
@ -1462,6 +1476,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else: else:
regs[ra].node = newNodeI(nkIdent, c.debug[pc]) regs[ra].node = newNodeI(nkIdent, c.debug[pc])
regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal) regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
regs[ra].node.flags.incl nfIsRef
of opcSetType: of opcSetType:
if regs[ra].kind != rkNode: if regs[ra].kind != rkNode:
internalError(c.config, c.debug[pc], "cannot set type") internalError(c.config, c.debug[pc], "cannot set type")

View file

@ -654,17 +654,17 @@ template replaceImpl(str: string, pattern: Regex,
proc replace*(str: string, pattern: Regex, proc replace*(str: string, pattern: Regex,
subproc: proc (match: RegexMatch): string): string = subproc: proc (match: RegexMatch): string): string =
## Replaces each match of Regex in the string with ``sub``, which should ## Replaces each match of Regex in the string with ``subproc``, which should
## never be or return ``nil``. ## never be or return ``nil``.
## ##
## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with ## If ``subproc`` is a ``proc (RegexMatch): string``, then it is executed with
## each match and the return value is the replacement value. ## each match and the return value is the replacement value.
## ##
## If ``sub`` is a ``proc (string): string``, then it is executed with the ## If ``subproc`` is a ``proc (string): string``, then it is executed with the
## full text of the match and and the return value is the replacement ## full text of the match and and the return value is the replacement
## value. ## value.
## ##
## If ``sub`` is a string, the syntax is as follows: ## If ``subproc`` is a string, the syntax is as follows:
## ##
## - ``$$`` - literal ``$`` ## - ``$$`` - literal ``$``
## - ``$123`` - capture number ``123`` ## - ``$123`` - capture number ``123``

View file

@ -884,11 +884,9 @@ elif not defined(useNimRtl):
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx) chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx]) chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx) chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
if (poStdErrToStdOut in data.options): if (poStdErrToStdOut in data.options):
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2) chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
else:
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
var res: cint var res: cint
if data.workingDir.len > 0: if data.workingDir.len > 0:

View file

@ -258,7 +258,9 @@ template callFormat(res, arg) {.dirty.} =
# workaround in order to circumvent 'strutils.format' which matches # workaround in order to circumvent 'strutils.format' which matches
# too but doesn't adhere to our protocol. # too but doesn't adhere to our protocol.
res.add arg res.add arg
elif compiles(format(arg, res)): elif compiles(format(arg, res)) and
# Check if format returns void
not (compiles do: discard format(arg, res)):
format(arg, res) format(arg, res)
elif compiles(format(arg)): elif compiles(format(arg)):
res.add format(arg) res.add format(arg)
@ -684,6 +686,9 @@ when isMainModule:
var nullTime: DateTime var nullTime: DateTime
check &"{nullTime:yyyy-mm-dd}", "0000-00-00" check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
var tm = fromUnix(0)
discard &"{tm}"
# Unicode string tests # Unicode string tests
check &"""{"αβγ"}""", "αβγ" check &"""{"αβγ"}""", "αβγ"
check &"""{"αβγ":>5}""", " αβγ" check &"""{"αβγ":>5}""", " αβγ"

View file

@ -489,18 +489,16 @@ proc toParts*(dur: Duration): DurationParts =
result[unit] = quantity result[unit] = quantity
proc stringifyUnit*(value: int | int64, unit: string): string = proc stringifyUnit(value: int | int64, unit: TimeUnit): string =
## Stringify time unit with it's name, lowercased ## Stringify time unit with it's name, lowercased
runnableExamples: let strUnit = $unit
doAssert stringifyUnit(2, "Seconds") == "2 seconds"
doAssert stringifyUnit(1, "Years") == "1 year"
result = "" result = ""
result.add($value) result.add($value)
result.add(" ") result.add(" ")
if abs(value) != 1: if abs(value) != 1:
result.add(unit.toLowerAscii()) result.add(strUnit.toLowerAscii())
else: else:
result.add(unit[0..^2].toLowerAscii()) result.add(strUnit[0..^2].toLowerAscii())
proc humanizeParts(parts: seq[string]): string = proc humanizeParts(parts: seq[string]): string =
## Make date string parts human-readable ## Make date string parts human-readable
@ -530,7 +528,7 @@ proc `$`*(dur: Duration): string =
for unit in countdown(Weeks, Nanoseconds): for unit in countdown(Weeks, Nanoseconds):
let quantity = numParts[unit] let quantity = numParts[unit]
if quantity != 0.int64: if quantity != 0.int64:
parts.add(stringifyUnit(quantity, $unit)) parts.add(stringifyUnit(quantity, unit))
result = humanizeParts(parts) result = humanizeParts(parts)
@ -1024,7 +1022,7 @@ proc `$`*(ti: TimeInterval): string =
var tiParts = toParts(ti) var tiParts = toParts(ti)
for unit in countdown(Years, Nanoseconds): for unit in countdown(Years, Nanoseconds):
if tiParts[unit] != 0: if tiParts[unit] != 0:
parts.add(stringifyUnit(tiParts[unit], $unit)) parts.add(stringifyUnit(tiParts[unit], unit))
result = humanizeParts(parts) result = humanizeParts(parts)

12
tests/concepts/t7952.nim Normal file
View file

@ -0,0 +1,12 @@
discard """
output: 5
"""
type
HasLen = concept iter
len(iter) is int
proc echoLen(x: HasLen) =
echo len(x)
echoLen([1, 2, 3, 4, 5])

View file

@ -151,3 +151,13 @@ if true:
fooBool() fooBool()
else: else:
raise newException(ValueError, "argh") raise newException(ValueError, "argh")
# bug #5374
proc test1(): int64 {.discardable.} = discard
proc test2(): int {.discardable.} = discard
if true:
test1()
else:
test2()

7
tests/iter/t8041.nim Normal file
View file

@ -0,0 +1,7 @@
iterator xy[T](a: T, b: set[T]): T =
if a in b:
yield a
for a in xy(1'i8, {}):
for b in xy(a, {}):
echo a

View file

@ -138,3 +138,11 @@ block:
assert hasIntSerKey assert hasIntSerKey
assert strSerKey == "string" assert strSerKey == "string"
assert nestedItemDefVal == "Nimmers of the world, unite!" assert nestedItemDefVal == "Nimmers of the world, unite!"
block:
template simpleAttr {.pragma.}
type Annotated {.simpleAttr.} = object
proc generic_proc[T]() =
assert Annotated.hasCustomPragma(simpleAttr)

6
tests/sets/t2669.nim Normal file
View file

@ -0,0 +1,6 @@
discard """
line: 6
errormsg: "cannot convert 6 to range 1..5(int8)"
"""
var c: set[range[1i8..5i8]] = {1i8, 2i8, 6i8}

View file

@ -90,3 +90,61 @@ block:
var some = newStringOfCap(size) var some = newStringOfCap(size)
result = size result = size
doAssert f(4) == 4 doAssert f(4) == 4
# #6689
block:
static:
proc foo(): int = 0
var f: proc(): int
doAssert f.isNil
f = foo
doAssert(not f.isNil)
block:
static:
var x: ref ref int
new(x)
doAssert(not x.isNil)
# #7871
static:
type Obj = object
field: int
var s = newSeq[Obj](1)
var o = Obj()
s[0] = o
o.field = 2
doAssert s[0].field == 0
# #8125
static:
let def_iter_var = ident("it")
# #8142
static:
type Obj = object
names: string
proc pushName(o: var Obj) =
var s = ""
s.add("FOOBAR")
o.names.add(s)
var o = Obj()
o.names = ""
o.pushName()
o.pushName()
doAssert o.names == "FOOBARFOOBAR"
# #8154
import parseutils
static:
type Obj = object
i: int
proc foo(): Obj =
discard parseInt("1", result.i, 0)
static:
doAssert foo().i == 1