Merge branch 'devel' into araq-devel

This commit is contained in:
Andreas Rumpf
2018-07-01 16:24:56 +02:00
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]
initLocExpr(p, ea, a)
initLoc(b, locExpr, e, OnUnknown)
b.r = rope("(")
var length = sonsLen(e.sons[1])
for i in countup(0, length - 1):
let it = e.sons[1].sons[i]
if it.kind == nkRange:
initLocExpr(p, it.sons[0], x)
initLocExpr(p, it.sons[1], y)
addf(b.r, "$1 >= $2 && $1 <= $3",
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
else:
initLocExpr(p, it, x)
addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
if i < length - 1: add(b.r, " || ")
add(b.r, ")")
if length > 0:
b.r = rope("(")
for i in countup(0, length - 1):
let it = e.sons[1].sons[i]
if it.kind == nkRange:
initLocExpr(p, it.sons[0], x)
initLocExpr(p, it.sons[1], y)
addf(b.r, "$1 >= $2 && $1 <= $3",
[rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
else:
initLocExpr(p, it, x)
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)
else:
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+1] notin chars:
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
add(tok.literal, '_')
inc(pos)

View File

@@ -76,7 +76,7 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
result = arg
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)
else:
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
# not allow regular values to be matched against the type:
liftLhs = false
else:
n.sons[2] = semExpr(c, n[2])
var lhsType = n[1].typ
if lhsType.kind != tyTypeDesc:
@@ -906,19 +908,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
proc semExprNoType(c: PContext, n: PNode): PNode =
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)
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)
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 =
let s = sym

View File

@@ -111,13 +111,15 @@ proc semExprBranchScope(c: PContext, n: PNode): PNode =
const
skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr}
nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
nkHiddenStdConv}
proc implicitlyDiscardable(n: PNode): bool =
var n = n
while n.kind in skipForDiscardable: n = n.lastSon
result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags
result = n.kind == nkRaiseStmt or
(isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags)
proc fixNilType(c: PContext; n: PNode) =
if isAtom(n):
@@ -132,11 +134,8 @@ proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if implicitlyDiscardable(result):
var n = result
result.typ = nil
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
var n = newNodeI(nkDiscardStmt, result.info, 1)
n[0] = result
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
var n = result
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)
result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred, tyUserTypeClassInst:
tyStatic, tyInferred, tyUserTypeClasses:
result = firstOrd(conf, lastSon(t))
of tyOrdinal:
if t.len > 0: result = firstOrd(conf, lastSon(t))
@@ -642,7 +642,7 @@ proc firstFloat*(t: PType): BiggestFloat =
getFloatValue(t.n.sons[0])
of tyVar: firstFloat(t.sons[0])
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred:
tyStatic, tyInferred, tyUserTypeClasses:
firstFloat(lastSon(t))
else:
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)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred:
tyStatic, tyInferred, tyUserTypeClasses:
result = lastOrd(conf, lastSon(t))
of tyProxy: result = 0
of tyOrdinal:
@@ -699,7 +699,7 @@ proc lastFloat*(t: PType): BiggestFloat =
assert(t.n.kind == nkRange)
getFloatValue(t.n.sons[1])
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
tyStatic, tyInferred:
tyStatic, tyInferred, tyUserTypeClasses:
lastFloat(lastSon(t))
else:
internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
@@ -707,7 +707,7 @@ proc lastFloat*(t: PType): BiggestFloat =
proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
case t.kind
case t.skipTypes(tyUserTypeClasses).kind
of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
of tyDistinct: result = lengthOrd(conf, t.sons[0])
else:
@@ -717,7 +717,7 @@ proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
if last == high(BiggestInt) and first <= 0:
result = last
else:
result = lastOrd(conf, t) - firstOrd(conf, t) + 1
result = last - first + 1
# -------------- type equality -----------------------------------------------

View File

@@ -209,7 +209,7 @@ proc writeField(n: var PNode, x: TFullReg) =
of rkNone: discard
of rkInt: n.intVal = x.intVal
of rkFloat: n.floatVal = x.floatVal
of rkNode: n = x.node
of rkNode: n = copyValue(x.node)
of rkRegisterAddr: writeField(n, x.regAddr[])
of rkNodeAddr: n = x.nodeAddr[]
@@ -622,6 +622,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
stackTrace(c, tos, pc, errNilAccess)
regs[ra].nodeAddr[][] = n[]
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:
regs[ra].nodeAddr[] = n
of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
@@ -912,6 +919,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if a.kind == nkSym:
regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit)
else: copyTree(a.sym.ast)
regs[ra].node.flags.incl nfIsRef
else:
stackTrace(c, tos, pc, "node is not a symbol")
of opcEcho:
@@ -1210,8 +1218,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcIsNil:
decodeB(rkInt)
let node = regs[rb].node
regs[ra].intVal = ord(node.kind == nkNilLit or
(node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil))
regs[ra].intVal = ord(
# 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:
decodeBx(rkNode)
regs[ra].node = copyTree(c.constants.sons[rbx])
@@ -1462,6 +1476,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
else:
regs[ra].node = newNodeI(nkIdent, c.debug[pc])
regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
regs[ra].node.flags.incl nfIsRef
of opcSetType:
if regs[ra].kind != rkNode:
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,
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``.
##
## 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.
##
## 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
## value.
##
## If ``sub`` is a string, the syntax is as follows:
## If ``subproc`` is a string, the syntax is as follows:
##
## - ``$$`` - literal ``$``
## - ``$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_addclose(fops, data.pStdout[readIdx])
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):
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
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
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
# too but doesn't adhere to our protocol.
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)
elif compiles(format(arg)):
res.add format(arg)
@@ -684,6 +686,9 @@ when isMainModule:
var nullTime: DateTime
check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
var tm = fromUnix(0)
discard &"{tm}"
# Unicode string tests
check &"""{"αβγ"}""", "αβγ"
check &"""{"αβγ":>5}""", " αβγ"

View File

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

@@ -137,4 +137,12 @@ block:
static:
assert hasIntSerKey
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

@@ -89,4 +89,62 @@ block:
proc f(size: int): int =
var some = newStringOfCap(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