mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-04 14:38:38 +00:00
Merge branch 'devel' into pr_field
This commit is contained in:
43
tests/arc/t25595.nim
Normal file
43
tests/arc/t25595.nim
Normal file
@@ -0,0 +1,43 @@
|
||||
discard """
|
||||
matrix: "--mm:orc; --mm:arc; --mm:refc"
|
||||
"""
|
||||
|
||||
# bug #25595: cursor inference must not borrow a case object whose source can be
|
||||
# mutated through the cursor's own ref across a call. `let c = h.w` was inferred as a
|
||||
# non-owning cursor; `clear(c.r)` overwrites `h.w` via the cursor's back-reference,
|
||||
# freeing the ref while the borrow still uses it -> use-after-free. Detected here
|
||||
# deterministically: the element's destructor must not run during the call.
|
||||
|
||||
var destroyed = false
|
||||
|
||||
type
|
||||
O = ref object
|
||||
value: int
|
||||
home: H
|
||||
W = object
|
||||
case k: bool
|
||||
of true: r: O
|
||||
of false: discard
|
||||
H = ref object
|
||||
w: W
|
||||
|
||||
proc `=destroy`(o: var typeof(O()[])) =
|
||||
destroyed = true
|
||||
|
||||
proc clear(o: O): int =
|
||||
o.home.w = W()
|
||||
doAssert not destroyed, "use-after-free: element destroyed during the call"
|
||||
result = o.value
|
||||
|
||||
proc go(h: H): int =
|
||||
let c = h.w
|
||||
result = clear(c.r)
|
||||
|
||||
proc main =
|
||||
let h = H()
|
||||
let o = O(value: 42)
|
||||
o.home = h
|
||||
h.w = W(k: true, r: o)
|
||||
doAssert go(h) == 42
|
||||
|
||||
main()
|
||||
47
tests/arc/t25850.nim
Normal file
47
tests/arc/t25850.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
cmd: '''nim c --mm:orc --expandArc:uIf --expandArc:uCase $file'''
|
||||
nimout: '''
|
||||
--expandArc: uIf
|
||||
|
||||
block :tmp:
|
||||
let s = w()
|
||||
if true:
|
||||
r[] = s
|
||||
else:
|
||||
r[] = s
|
||||
-- end of expandArc ------------------------
|
||||
--expandArc: uCase
|
||||
|
||||
block :tmp:
|
||||
let s = w()
|
||||
case n
|
||||
of 0:
|
||||
r[] = s
|
||||
else:
|
||||
r[] = w()
|
||||
-- end of expandArc ------------------------
|
||||
'''
|
||||
"""
|
||||
|
||||
# bug #25850
|
||||
# Assigning an expression-based control flow construct (an `if`/`case` nested in
|
||||
# a `block`) must distribute the assignment directly into the leaf branches
|
||||
# instead of creating redundant intermediate temporaries per branch.
|
||||
|
||||
proc w(): array[1000, byte] {.noinline.} = discard
|
||||
|
||||
proc uIf(r: ptr array[1000, byte]) =
|
||||
r[] = (block:
|
||||
let s = w()
|
||||
if true: s else: s)
|
||||
|
||||
proc uCase(r: ptr array[1000, byte], n: int) =
|
||||
r[] = (block:
|
||||
let s = w()
|
||||
case n
|
||||
of 0: s
|
||||
else: w())
|
||||
|
||||
var d: array[1000, byte]
|
||||
uIf(addr d)
|
||||
uCase(addr d, 0)
|
||||
36
tests/ccg/tclosure_err_panic_goto.nim
Normal file
36
tests/ccg/tclosure_err_panic_goto.nim
Normal file
@@ -0,0 +1,36 @@
|
||||
discard """
|
||||
matrix: "; --panics:on"
|
||||
"""
|
||||
# issue #25851: --panics:on must not drop the nimErr_ check after a closure
|
||||
# call whose result is consumed directly (e.g. `result.add elem(src)`).
|
||||
# Regression from #25295.
|
||||
|
||||
type
|
||||
Overrun = object of CatchableError
|
||||
Source = object
|
||||
data: seq[bool]
|
||||
cursor: int
|
||||
ElemFn = proc(src: var Source): bool {.closure.}
|
||||
|
||||
proc drawBool(src: var Source): bool =
|
||||
if src.cursor >= src.data.len: raise newException(Overrun, "exhausted")
|
||||
result = src.data[src.cursor]; inc src.cursor
|
||||
|
||||
proc listRun(elem: ElemFn, src: var Source): seq[bool] =
|
||||
result = @[]
|
||||
while true:
|
||||
if not src.drawBool(): break
|
||||
result.add elem(src) # closure call – the result flows straight
|
||||
# into `add`, which previously caused the
|
||||
# compiler to skip the nimErr_ check.
|
||||
|
||||
let elem: ElemFn = proc(src: var Source): bool = src.drawBool()
|
||||
|
||||
# Both --panics:on and --panics:off must propagate the Overrun.
|
||||
var caught = false
|
||||
try:
|
||||
var src = Source(data: @[true])
|
||||
discard listRun(elem, src)
|
||||
except Overrun:
|
||||
caught = true
|
||||
doAssert caught, "Overrun exception was swallowed"
|
||||
13
tests/ccgbugs2/m25294/c.nim
Normal file
13
tests/ccgbugs2/m25294/c.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
template a(T: type): int =
|
||||
when T is uint64: 1 else: 2
|
||||
|
||||
type
|
||||
M*[T] = object
|
||||
data*: seq[T]
|
||||
b: seq[int]
|
||||
indices*: array[a(T), int64]
|
||||
U = distinct uint64
|
||||
D* = object
|
||||
c: M[U]
|
||||
v: array[180000, int64]
|
||||
g*: M[uint64]
|
||||
5
tests/ccgbugs2/m25294/t.nim
Normal file
5
tests/ccgbugs2/m25294/t.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
import ./c
|
||||
|
||||
proc p*(): D =
|
||||
let c = M[uint64](data: @[0], indices: [1])
|
||||
result = D(g: c)
|
||||
7
tests/ccgbugs2/m25800.h
Normal file
7
tests/ccgbugs2/m25800.h
Normal file
@@ -0,0 +1,7 @@
|
||||
/*TYPESECTION*/
|
||||
struct CppRef {
|
||||
int* data;
|
||||
CppRef() : data(new int(42)) {}
|
||||
~CppRef() { delete data; data = nullptr; }
|
||||
void reset() { delete data; data = nullptr; }
|
||||
};
|
||||
18
tests/ccgbugs2/t25294.nim
Normal file
18
tests/ccgbugs2/t25294.nim
Normal file
@@ -0,0 +1,18 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
"""
|
||||
|
||||
import ./m25294/[c, t]
|
||||
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
23
tests/ccgbugs2/t25800.nim
Normal file
23
tests/ccgbugs2/t25800.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
# Bug Report 1: {.importcpp.} on =wasMoved generates invalid preprocessor directive #.
|
||||
|
||||
|
||||
type CppRef* {.importcpp, bycopy, noInit, header: "m25800.h".} = object
|
||||
|
||||
proc `=destroy`(x: var CppRef) {.importcpp: "#.~CppRef()".}
|
||||
proc `=wasMoved`(x: var CppRef) {.importcpp: "#.reset()".}
|
||||
proc `=copy`(dest: var CppRef; src: CppRef) {.importcpp: "dest = src".}
|
||||
proc `=sink`(dest: var CppRef; src: CppRef) {.importcpp: "dest = std::move(src)".}
|
||||
|
||||
# This triggers =wasMoved when passing to sink parameter
|
||||
proc consume(x: sink CppRef) = discard
|
||||
|
||||
proc test() =
|
||||
var x: CppRef
|
||||
consume(move(x)) # =wasMoved MUST be called here after the move
|
||||
|
||||
test()
|
||||
@@ -12,3 +12,51 @@ proc foo =
|
||||
doAssert m.id == 999
|
||||
|
||||
foo()
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
a,b,c: int
|
||||
|
||||
var dest: Foo
|
||||
|
||||
# proc `=wasMoved`(x: var Foo) =
|
||||
# debugEcho "wasMoved called"
|
||||
|
||||
proc main() =
|
||||
var x = Foo(a:11, b:12, c:13)
|
||||
dest = move(x)
|
||||
|
||||
main()
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
a,b,c: int
|
||||
|
||||
var dest: Foo
|
||||
|
||||
proc `=wasMoved`(x: var Foo) =
|
||||
discard "wasMoved called"
|
||||
|
||||
proc main() =
|
||||
var x = Foo(a:11, b:12, c:13)
|
||||
dest = move(x)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
import std/threadpool
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
data: string
|
||||
|
||||
proc `=wasMoved`(x: var Foo) =
|
||||
discard
|
||||
|
||||
proc work(x: Foo) =
|
||||
discard
|
||||
|
||||
var x = Foo(data: "hello")
|
||||
spawn work(x)
|
||||
sync()
|
||||
|
||||
|
||||
74
tests/effects/tcase_raises.nim
Normal file
74
tests/effects/tcase_raises.nim
Normal file
@@ -0,0 +1,74 @@
|
||||
from std/os import osLastError, osErrorMsg, OSErrorCode, raiseOSError,
|
||||
newOSError, `==`
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
const
|
||||
EPERM* = OSErrorCode(1)
|
||||
ECONNABORTED* = OSErrorCode(53)
|
||||
ETIMEDOUT* = OSErrorCode(60)
|
||||
ENOTCONN* = OSErrorCode(107)
|
||||
EMFILE* = OSErrorCode(24)
|
||||
ENFILE* = OSErrorCode(23)
|
||||
ENOBUFS* = OSErrorCode(55)
|
||||
ENOMEM* = OSErrorCode(12)
|
||||
|
||||
type
|
||||
AsyncError* = object of CatchableError
|
||||
TransportErrorBase* = object of AsyncError
|
||||
TransportOsError* = object of TransportErrorBase
|
||||
code*: OSErrorCode
|
||||
TransportTooManyError* = object of TransportErrorBase
|
||||
TransportAbortedError* = object of TransportErrorBase
|
||||
|
||||
template getConnectionAbortedError*(
|
||||
code: OSErrorCode
|
||||
): ref TransportAbortedError =
|
||||
let msg =
|
||||
case code
|
||||
of OSErrorCode(0), ECONNABORTED:
|
||||
"[ECONNABORTED] Connection has been aborted before being accepted"
|
||||
of EPERM:
|
||||
"[EPERM] Firewall rules forbid connection"
|
||||
of ETIMEDOUT:
|
||||
"[ETIMEDOUT] Operation has been timed out"
|
||||
of ENOTCONN:
|
||||
"[ENOTCONN] Transport endpoint is not connected"
|
||||
else:
|
||||
"[" & $int(code) & "] Connection has been aborted"
|
||||
newException(TransportAbortedError, msg)
|
||||
|
||||
template getTransportTooManyError*(
|
||||
code = OSErrorCode(0)
|
||||
): ref TransportTooManyError =
|
||||
let msg =
|
||||
case code
|
||||
of OSErrorCode(0):
|
||||
"Too many open transports"
|
||||
of EMFILE:
|
||||
"[EMFILE] Too many open files in the process"
|
||||
of ENFILE:
|
||||
"[ENFILE] Too many open files in system"
|
||||
of ENOBUFS:
|
||||
"[ENOBUFS] No buffer space available"
|
||||
of ENOMEM:
|
||||
"[ENOMEM] Not enough memory availble"
|
||||
else:
|
||||
"[" & $int(code) & "] Too many open transports"
|
||||
newException(TransportTooManyError, msg)
|
||||
|
||||
template getTransportError*(ecode: OSErrorCode): untyped =
|
||||
case ecode
|
||||
of ECONNABORTED, EPERM, ETIMEDOUT, ENOTCONN:
|
||||
getConnectionAbortedError(ecode)
|
||||
of EMFILE, ENFILE, ENOBUFS, ENOMEM:
|
||||
getTransportTooManyError(ecode)
|
||||
else:
|
||||
(ref TransportOsError)(code: ecode,
|
||||
msg: "(" & $int(ecode) & ") " & osErrorMsg(ecode))
|
||||
|
||||
proc raiseTransportError*(err: OSErrorCode) {.
|
||||
raises: [TransportAbortedError, TransportTooManyError, TransportOsError],
|
||||
noreturn.} =
|
||||
## Raises transport specific OS error.
|
||||
raise getTransportError(err)
|
||||
8
tests/effects/tcast_effect_violation.nim
Normal file
8
tests/effects/tcast_effect_violation.nim
Normal file
@@ -0,0 +1,8 @@
|
||||
discard """
|
||||
errormsg: "cast(raises: ValueError) can raise an unlisted exception: ValueError"
|
||||
line: 7
|
||||
"""
|
||||
|
||||
proc fff() {.raises: [].} =
|
||||
{.cast(raises: ValueError).}:
|
||||
discard
|
||||
30
tests/exception/treraise_typeless_except_finally.nim
Normal file
30
tests/exception/treraise_typeless_except_finally.nim
Normal file
@@ -0,0 +1,30 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
matrix: "--mm:arc; --mm:orc; --mm:refc"
|
||||
output: '''
|
||||
finally
|
||||
after
|
||||
'''
|
||||
"""
|
||||
|
||||
# Regression test: typeless `except:` followed by `finally:` must not
|
||||
# trigger ReraiseDefect at the end of the proc.
|
||||
#
|
||||
# Previously, `genTryCpp` only emitted `T_ = nullptr;` in the *typed*
|
||||
# except branches, leaving the typeless `except:` path with a still-set
|
||||
# `T_`. After the handler body and `popCurrentException`, the trailing
|
||||
# `if (T_) std::rethrow_exception(T_);` in the finally block would still
|
||||
# fire — but with the Nim exception stack already popped, the rethrow
|
||||
# bubbled up as a `ReraiseDefect: no exception to reraise`.
|
||||
|
||||
proc test() =
|
||||
try:
|
||||
raise newException(CatchableError, "x")
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
discard e
|
||||
finally:
|
||||
echo "finally"
|
||||
|
||||
test()
|
||||
echo "after"
|
||||
16
tests/generics/t20811.nim
Normal file
16
tests/generics/t20811.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
output: '''42
|
||||
42'''
|
||||
"""
|
||||
|
||||
proc outer(j: int) =
|
||||
proc genericInner[T](): int =
|
||||
j
|
||||
|
||||
proc plainInner(): int =
|
||||
j
|
||||
|
||||
echo genericInner[int]()
|
||||
echo plainInner()
|
||||
|
||||
outer(42)
|
||||
19
tests/init/t25857.nim
Normal file
19
tests/init/t25857.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
discard """
|
||||
output: "1"
|
||||
"""
|
||||
|
||||
# Regression for #25857: `typeof(result)` inside `result`'s initializer must not be
|
||||
# treated as a use-before-initialization of `result`. `typeof` is a type query and
|
||||
# never evaluates its operand, so this compiles and runs.
|
||||
# (Before the fix this errored: "'result' requires explicit initialization" on
|
||||
# {.requiresInit.} return types, breaking the `ok(typeof(result), v)` idiom.)
|
||||
|
||||
type Box[T] {.requiresInit.} = object
|
||||
v: T
|
||||
|
||||
func make[T](_: typedesc[Box[T]], v: T): Box[T] = Box[T](v: v)
|
||||
|
||||
proc f(): Box[int] =
|
||||
make(typeof(result), 1)
|
||||
|
||||
echo f().v
|
||||
@@ -465,4 +465,24 @@ block: # bug #25724
|
||||
else: yield 1
|
||||
for w in c():
|
||||
let n = w
|
||||
(proc() = discard n)()
|
||||
(proc() = discard n)()
|
||||
|
||||
block:
|
||||
iterator c(): int =
|
||||
yield 1
|
||||
yield 1
|
||||
|
||||
for w in c():
|
||||
proc p(s: int) =
|
||||
let sap = s
|
||||
p(0)
|
||||
|
||||
block: # bug #25725
|
||||
iterator c(): int =
|
||||
when nimvm: yield 0
|
||||
else: yield 1
|
||||
for w in c():
|
||||
let n = w
|
||||
proc p(s: int) =
|
||||
let s = s; discard n
|
||||
p(0)
|
||||
|
||||
38
tests/lent/titems_array_lent.nim
Normal file
38
tests/lent/titems_array_lent.nim
Normal file
@@ -0,0 +1,38 @@
|
||||
discard """
|
||||
targets: "c cpp js"
|
||||
"""
|
||||
|
||||
template sameAddress(a, b): bool =
|
||||
when defined(js):
|
||||
a == b
|
||||
else:
|
||||
a.unsafeAddr == b.unsafeAddr
|
||||
|
||||
proc main() =
|
||||
block:
|
||||
let a = [10, 11, 12]
|
||||
for ai in items(a):
|
||||
doAssert sameAddress(ai, a[0])
|
||||
break
|
||||
|
||||
block:
|
||||
let a = [[1, 2], [1, 2], [1, 2]]
|
||||
for ai in items(a):
|
||||
doAssert sameAddress(ai[0], a[0][0])
|
||||
break
|
||||
|
||||
block:
|
||||
let s = @[(1, 2), (3, 4), (5, 6)]
|
||||
doAssert (3, 4) in s
|
||||
|
||||
main()
|
||||
|
||||
static:
|
||||
main()
|
||||
|
||||
block: # issue #25849
|
||||
static:
|
||||
const key = "NIM_TESTS_TOSENV_KEY"
|
||||
for val in ["val", "", "\xc3\x86"]:
|
||||
let s = @[(key, "val"), (key, ""), (key, "\xc3\x86")]
|
||||
doAssert (key, val) in s
|
||||
@@ -45,3 +45,13 @@ block:
|
||||
r: R
|
||||
|
||||
func f(o: O): int = 42
|
||||
|
||||
block:
|
||||
iterator j(x: array[1, int]): lent int = yield x[0]
|
||||
iterator g(): int {.closure.} =
|
||||
let a = 1
|
||||
for w in j([a]):
|
||||
yield 0
|
||||
doAssert w == 1
|
||||
for _ in g(): discard
|
||||
|
||||
|
||||
5
tests/method/mvtables_reentry_a.nim
Normal file
5
tests/method/mvtables_reentry_a.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
type
|
||||
VtableBaseA* = ref object of RootObj
|
||||
|
||||
method say*(a: VtableBaseA): string {.base.} =
|
||||
"base"
|
||||
7
tests/method/mvtables_reentry_b.nim
Normal file
7
tests/method/mvtables_reentry_b.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
import mvtables_reentry_a
|
||||
|
||||
type
|
||||
VtableDerivedB* = ref object of VtableBaseA
|
||||
|
||||
method say*(d: VtableDerivedB): string =
|
||||
"derived"
|
||||
19
tests/method/tvtable_reentry.nim
Normal file
19
tests/method/tvtable_reentry.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
discard """
|
||||
targets: "c cpp"
|
||||
"""
|
||||
|
||||
import mvtables_reentry_a
|
||||
|
||||
type
|
||||
MainType = ref object of VtableBaseA
|
||||
|
||||
method say*(m: MainType): string =
|
||||
"main"
|
||||
|
||||
when isMainModule:
|
||||
import mvtables_reentry_b
|
||||
|
||||
let a: VtableBaseA = VtableDerivedB()
|
||||
doAssert a.say() == "derived"
|
||||
let m: VtableBaseA = MainType()
|
||||
doAssert m.say() == "main"
|
||||
@@ -20,3 +20,25 @@ block: # issue #24021
|
||||
discard
|
||||
else:
|
||||
discard foo.z
|
||||
|
||||
|
||||
# bug #22791
|
||||
type Foo = object
|
||||
case a: bool
|
||||
of false:
|
||||
discard
|
||||
of true:
|
||||
case b: bool
|
||||
of false:
|
||||
discard
|
||||
of true:
|
||||
c: bool
|
||||
|
||||
const f = Foo(a: true, b: true, c: true)
|
||||
case f.a
|
||||
of true:
|
||||
case f.b
|
||||
of true:
|
||||
echo f.c
|
||||
else: discard
|
||||
else: discard
|
||||
12
tests/proc/tinvalid_cmp_op1.nim
Normal file
12
tests/proc/tinvalid_cmp_op1.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op1.nim(12, 1) Warning: define `<=` instead of `>=` to implement user defined comparison operator. it allows you to use `>=` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `>=`(a, b: Foo): bool = int(a) >= int(b)
|
||||
12
tests/proc/tinvalid_cmp_op2.nim
Normal file
12
tests/proc/tinvalid_cmp_op2.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op2.nim(12, 1) Warning: define `<` instead of `>` to implement user defined comparison operator. it allows you to use `>` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `>`(a, b: Foo): bool = int(a) > int(b)
|
||||
12
tests/proc/tinvalid_cmp_op3.nim
Normal file
12
tests/proc/tinvalid_cmp_op3.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op3.nim(12, 1) Warning: define `==` instead of `!=` to implement user defined comparison operator. it allows you to use `!=` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `!=`(a, b: Foo): bool = int(a) != int(b)
|
||||
@@ -29,3 +29,30 @@ block tnestprc:
|
||||
result = x + y
|
||||
result = add(x, 3)
|
||||
doAssert Add3(7) == 10
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
const u = A(c: 0)
|
||||
proc e(T: typedesc): lent A = u
|
||||
proc y(T: typedesc): H =
|
||||
proc(): lent A {.nimcall.} = T.e
|
||||
discard y(int)
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
let u = A(c: 0)
|
||||
proc y(_: int | int): H =
|
||||
proc(): lent A {.nimcall.} = u
|
||||
discard y(0)
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
let u = A()
|
||||
let _: H = proc(): lent A {.nimcall.} = u
|
||||
|
||||
|
||||
@@ -241,3 +241,12 @@ proc main() =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
# $ separator must be emitted even when the item's string repr is empty
|
||||
type EmptyStr18583 = object
|
||||
proc `$`(x: EmptyStr18583): string = ""
|
||||
|
||||
block:
|
||||
var d = [EmptyStr18583(), EmptyStr18583()].toDeque
|
||||
doAssert $d == "[, ]", "got: " & $d
|
||||
|
||||
@@ -104,3 +104,15 @@ template main() =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
type EmptyStr18583HeapQ = object
|
||||
proc `$`(x: EmptyStr18583HeapQ): string = ""
|
||||
proc `<`(a, b: EmptyStr18583HeapQ): bool = false
|
||||
|
||||
block:
|
||||
var h = initHeapQueue[EmptyStr18583HeapQ]()
|
||||
push(h, EmptyStr18583HeapQ())
|
||||
push(h, EmptyStr18583HeapQ())
|
||||
let s = $h
|
||||
doAssert s == "[, ]", "got: " & s
|
||||
|
||||
@@ -287,3 +287,14 @@ template main =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
type EmptyStr18583List = object
|
||||
proc `$`(x: EmptyStr18583List): string = ""
|
||||
|
||||
block:
|
||||
var L: SinglyLinkedList[EmptyStr18583List]
|
||||
L.prepend(EmptyStr18583List())
|
||||
L.prepend(EmptyStr18583List())
|
||||
let s = $L
|
||||
doAssert s == "[, ]", "got: " & s
|
||||
|
||||
@@ -259,6 +259,11 @@ block:
|
||||
doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
|
||||
doAssert(not match("456678", peg"(\letter)+"))
|
||||
|
||||
block:
|
||||
doAssert match("CAFÉ", peg"\i café")
|
||||
doAssert match("Café", peg"\i café")
|
||||
doAssert "two cafés: Café and CAFÉ".findAll(peg"\i café").len == 3
|
||||
|
||||
doAssert("var1 = key; var2 = key2".replacef(
|
||||
peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
|
||||
"var1<-keykey;var2<-key2key2")
|
||||
|
||||
@@ -544,6 +544,12 @@ proc main() =
|
||||
var x = 5
|
||||
doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02"
|
||||
doAssert x==7
|
||||
|
||||
block: # binary operators in interpolated expressions
|
||||
let n = 1
|
||||
doAssert &"{n-1}" == "0"
|
||||
doAssert fmt"{n-1}" == "0"
|
||||
|
||||
block: #curly bracket expressions and tuples
|
||||
proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value
|
||||
|
||||
|
||||
@@ -289,6 +289,15 @@ template main() =
|
||||
var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""}
|
||||
var foo1 = parseUri("http://example.com/foo?do=do&bar")
|
||||
doAssert foo == foo1
|
||||
block: # issue #19782: appends to existing query string
|
||||
var foo = parseUri("http://example.com/foo?existing=1") ? {"bar": "qux"}
|
||||
doAssert $foo == "http://example.com/foo?existing=1&bar=qux"
|
||||
block: # issue #19782: empty params list preserves existing query
|
||||
var foo = parseUri("http://example.com/foo?existing=1") ? {:}
|
||||
doAssert $foo == "http://example.com/foo?existing=1"
|
||||
block: # issue #19782: empty params on uri without query is a no-op
|
||||
var foo = parseUri("http://example.com/foo") ? {:}
|
||||
doAssert $foo == "http://example.com/foo"
|
||||
|
||||
block: # getDataUri, dataUriBase64
|
||||
doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64,"
|
||||
|
||||
@@ -135,6 +135,19 @@ block: # issue #22605 for templates, original complex example
|
||||
|
||||
doAssert g2(int) == "error"
|
||||
|
||||
block: # issue #20811
|
||||
template injectError(body: untyped): untyped =
|
||||
template error: untyped {.used, inject.} = "injected"
|
||||
body
|
||||
|
||||
proc outerOpen(error: string): string =
|
||||
injectError:
|
||||
proc genericInner[T](): string =
|
||||
error
|
||||
genericInner[int]()
|
||||
|
||||
doAssert outerOpen("captured") == "injected"
|
||||
|
||||
block: # issue #23865 for templates
|
||||
type Xxx = enum
|
||||
error
|
||||
|
||||
77
tests/template/toverload_over_untyped.nim
Normal file
77
tests/template/toverload_over_untyped.nim
Normal file
@@ -0,0 +1,77 @@
|
||||
discard """
|
||||
output: '''ok
|
||||
ok
|
||||
ok'''
|
||||
"""
|
||||
|
||||
# bug #25693
|
||||
|
||||
proc d() = discard @[0]
|
||||
|
||||
proc f(a: var seq[int], _: string) =
|
||||
let p = @[0]
|
||||
d()
|
||||
a = p
|
||||
|
||||
block: # scalar `untyped` parameter
|
||||
template g(b: untyped) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
proc g(_: int) = discard
|
||||
|
||||
let q = "a"
|
||||
g:
|
||||
var a: seq[int]
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
block: t()
|
||||
block: t()
|
||||
echo "ok"
|
||||
|
||||
block: # `typed` parameter captured and re-emitted: each emission gets its own
|
||||
# symbols, otherwise the destructor/liveness pass miscompiles the shared
|
||||
# local `a` and the program crashes at runtime
|
||||
template g(b: typed) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
let q = "a"
|
||||
g:
|
||||
var a: seq[int]
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
block: t()
|
||||
block: t()
|
||||
echo "ok"
|
||||
|
||||
block: # `varargs[untyped]` parameter takes the same pristine-AST path
|
||||
template g(b: varargs[untyped]) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
proc g(_: int) = discard
|
||||
|
||||
let q = "a"
|
||||
g:
|
||||
var a: seq[int]
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
block: t()
|
||||
block: t()
|
||||
echo "ok"
|
||||
@@ -5,3 +5,31 @@ type
|
||||
proc newFoo[T](): Foo[T] = Foo[T](newSeq[T]())
|
||||
|
||||
var x = newFoo[Bar[int]]()
|
||||
|
||||
# issue #22936
|
||||
|
||||
import std/macros
|
||||
|
||||
type
|
||||
InternalFutureBase = object of RootObj
|
||||
|
||||
FutureBase = ref object of InternalFutureBase
|
||||
|
||||
Future[T] = ref object of FutureBase
|
||||
internalValue: T
|
||||
|
||||
B[T, E] = ref object of Future[T]
|
||||
|
||||
proc take[F: Future](fut: F) = discard
|
||||
|
||||
proc takeMany[F: Future](futs: seq[F]) = discard
|
||||
|
||||
macro checkFutures[F: Future](futs: seq[F]): untyped =
|
||||
newEmptyNode()
|
||||
|
||||
var future: B[void, void]
|
||||
var futures: seq[B[void, void]]
|
||||
|
||||
take(future)
|
||||
takeMany(futures)
|
||||
checkFutures(futures)
|
||||
|
||||
16
tests/vm/t25849.nim
Normal file
16
tests/vm/t25849.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
targets: "c cpp js"
|
||||
"""
|
||||
|
||||
import std/os
|
||||
from std/sequtils import toSeq
|
||||
|
||||
iterator items(a: array[3, string]): lent string {.inline.} =
|
||||
for i in 0..2:
|
||||
yield a[i]
|
||||
|
||||
static:
|
||||
const key = "NIM_TESTS_TOSENV_KEY"
|
||||
for val in items(["a", "b", "c"]):
|
||||
putEnv(key, val)
|
||||
doAssert (key, val) in toSeq(envPairs())
|
||||
Reference in New Issue
Block a user