mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-05 11:24:08 +00:00
Merge branch 'devel' into araq-devel
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -----------------------------------------------
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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``
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}""", " αβγ"
|
||||
|
||||
@@ -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
12
tests/concepts/t7952.nim
Normal 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])
|
||||
@@ -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
7
tests/iter/t8041.nim
Normal 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
|
||||
@@ -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
6
tests/sets/t2669.nim
Normal 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}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user