renamed '=' to '=copy' [backport:1.2] (#15585)
* Assign hook name changed to `=copy` * Adapt destructors.rst * [nobackport] Duplicate tests for =copy hook * Fix tests * added a changelog entry Co-authored-by: Clyybber <darkmine956@gmail.com>
This commit is contained in:
parent
42c180c665
commit
da4aa2e1fb
17 changed files with 1001 additions and 29 deletions
|
|
@ -215,6 +215,10 @@
|
||||||
|
|
||||||
- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
|
- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
|
||||||
`wasMoved` calls where needed.
|
`wasMoved` calls where needed.
|
||||||
|
- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
|
||||||
|
is no need to update your code. This change was backported to 1.2 too so you can use the
|
||||||
|
more readability `=copy` without loss of compatibility.
|
||||||
|
|
||||||
- In the newruntime it is now allowed to assign to the discriminator field
|
- In the newruntime it is now allowed to assign to the discriminator field
|
||||||
without restrictions as long as case object doesn't have custom destructor.
|
without restrictions as long as case object doesn't have custom destructor.
|
||||||
The discriminator value doesn't have to be a constant either. If you have a
|
The discriminator value doesn't have to be a constant either. If you have a
|
||||||
|
|
|
||||||
|
|
@ -1331,7 +1331,7 @@ const
|
||||||
MaxLockLevel* = 1000'i16
|
MaxLockLevel* = 1000'i16
|
||||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||||
"=destroy", "=", "=sink", "=trace", "=dispose", "=deepcopy"]
|
"=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"]
|
||||||
|
|
||||||
proc `$`*(x: TLockLevel): string =
|
proc `$`*(x: TLockLevel): string =
|
||||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ template isUnpackedTuple(n: PNode): bool =
|
||||||
|
|
||||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||||
if opname == "=" and ri != nil:
|
if (opname == "=" or opname == "=copy") and ri != nil:
|
||||||
m.add "; requires a copy because it's not the last read of '"
|
m.add "; requires a copy because it's not the last read of '"
|
||||||
m.add renderTree(ri)
|
m.add renderTree(ri)
|
||||||
m.add '\''
|
m.add '\''
|
||||||
|
|
@ -319,7 +319,7 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
||||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||||
# try to improve the error message here:
|
# try to improve the error message here:
|
||||||
if c.otherRead == nil: discard isLastRead(ri, c)
|
if c.otherRead == nil: discard isLastRead(ri, c)
|
||||||
c.checkForErrorPragma(t, ri, "=")
|
c.checkForErrorPragma(t, ri, "=copy")
|
||||||
result = c.genCopyNoCheck(dest, ri)
|
result = c.genCopyNoCheck(dest, ri)
|
||||||
|
|
||||||
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -809,7 +809,8 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||||
|
|
||||||
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
|
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
|
||||||
tracked.owner.kind != skMacro:
|
tracked.owner.kind != skMacro:
|
||||||
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
|
var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
|
||||||
|
if a.sym.name.s.normalize == "=": opKind = attachedAsgn.int
|
||||||
if opKind != -1:
|
if opKind != -1:
|
||||||
# rebind type bounds operations after createTypeBoundOps call
|
# rebind type bounds operations after createTypeBoundOps call
|
||||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||||
|
|
|
||||||
|
|
@ -1732,7 +1732,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
incl(s.flags, sfOverriden)
|
incl(s.flags, sfOverriden)
|
||||||
of "=", "=sink":
|
of "=", "=copy", "=sink":
|
||||||
if s.magic == mAsgn: return
|
if s.magic == mAsgn: return
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
incl(s.flags, sfOverriden)
|
incl(s.flags, sfOverriden)
|
||||||
|
|
@ -1754,7 +1754,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||||
# attach these ops to the canonical tySequence
|
# attach these ops to the canonical tySequence
|
||||||
obj = canonType(c, obj)
|
obj = canonType(c, obj)
|
||||||
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
||||||
let k = if name == "=": attachedAsgn else: attachedSink
|
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
|
||||||
if obj.attachedOps[k] == s:
|
if obj.attachedOps[k] == s:
|
||||||
discard "forward declared op"
|
discard "forward declared op"
|
||||||
elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags:
|
elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags:
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ written as:
|
||||||
for i in 0..<x.len: `=destroy`(x[i])
|
for i in 0..<x.len: `=destroy`(x[i])
|
||||||
dealloc(x.data)
|
dealloc(x.data)
|
||||||
|
|
||||||
proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
|
proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) =
|
||||||
# do nothing for self-assignments:
|
# do nothing for self-assignments:
|
||||||
if a.data == b.data: return
|
if a.data == b.data: return
|
||||||
`=destroy`(a)
|
`=destroy`(a)
|
||||||
|
|
@ -134,7 +134,7 @@ not free the resources afterwards by setting the object to its default value
|
||||||
default value is written as ``wasMoved(x)``. When not provided the compiler
|
default value is written as ``wasMoved(x)``. When not provided the compiler
|
||||||
is using a combination of `=destroy` and `copyMem` instead. This is efficient
|
is using a combination of `=destroy` and `copyMem` instead. This is efficient
|
||||||
hence users rarely need to implement their own `=sink` operator, it is enough to
|
hence users rarely need to implement their own `=sink` operator, it is enough to
|
||||||
provide `=destroy` and `=`, compiler will take care about the rest.
|
provide `=destroy` and `=copy`, compiler will take care about the rest.
|
||||||
|
|
||||||
The prototype of this hook for a type ``T`` needs to be:
|
The prototype of this hook for a type ``T`` needs to be:
|
||||||
|
|
||||||
|
|
@ -157,10 +157,10 @@ The general pattern in ``=sink`` looks like:
|
||||||
How self-assignments are handled is explained later in this document.
|
How self-assignments are handled is explained later in this document.
|
||||||
|
|
||||||
|
|
||||||
`=` (copy) hook
|
`=copy` hook
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The ordinary assignment in Nim conceptually copies the values. The ``=`` hook
|
The ordinary assignment in Nim conceptually copies the values. The ``=copy`` hook
|
||||||
is called for assignments that couldn't be transformed into ``=sink``
|
is called for assignments that couldn't be transformed into ``=sink``
|
||||||
operations.
|
operations.
|
||||||
|
|
||||||
|
|
@ -168,14 +168,14 @@ The prototype of this hook for a type ``T`` needs to be:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc `=`(dest: var T; source: T)
|
proc `=copy`(dest: var T; source: T)
|
||||||
|
|
||||||
|
|
||||||
The general pattern in ``=`` looks like:
|
The general pattern in ``=copy`` looks like:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc `=`(dest: var T; source: T) =
|
proc `=copy`(dest: var T; source: T) =
|
||||||
# protect against self-assignments:
|
# protect against self-assignments:
|
||||||
if dest.field != source.field:
|
if dest.field != source.field:
|
||||||
`=destroy`(dest)
|
`=destroy`(dest)
|
||||||
|
|
@ -183,7 +183,7 @@ The general pattern in ``=`` looks like:
|
||||||
dest.field = duplicateResource(source.field)
|
dest.field = duplicateResource(source.field)
|
||||||
|
|
||||||
|
|
||||||
The ``=`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
The ``=copy`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
||||||
that otherwise would lead to a copy is prevented at compile-time.
|
that otherwise would lead to a copy is prevented at compile-time.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -201,7 +201,7 @@ Swap
|
||||||
====
|
====
|
||||||
|
|
||||||
The need to check for self-assignments and also the need to destroy previous
|
The need to check for self-assignments and also the need to destroy previous
|
||||||
objects inside ``=`` and ``=sink`` is a strong indicator to treat
|
objects inside ``=copy`` and ``=sink`` is a strong indicator to treat
|
||||||
``system.swap`` as a builtin primitive of its own that simply swaps every
|
``system.swap`` as a builtin primitive of its own that simply swaps every
|
||||||
field in the involved objects via ``copyMem`` or a comparable mechanism.
|
field in the involved objects via ``copyMem`` or a comparable mechanism.
|
||||||
In other words, ``swap(a, b)`` is **not** implemented
|
In other words, ``swap(a, b)`` is **not** implemented
|
||||||
|
|
@ -326,7 +326,7 @@ destroyed at the scope exit.
|
||||||
|
|
||||||
x = y
|
x = y
|
||||||
------------------ (copy)
|
------------------ (copy)
|
||||||
`=`(x, y)
|
`=copy`(x, y)
|
||||||
|
|
||||||
|
|
||||||
f_sink(g())
|
f_sink(g())
|
||||||
|
|
@ -336,7 +336,7 @@ destroyed at the scope exit.
|
||||||
|
|
||||||
f_sink(notLastReadOf y)
|
f_sink(notLastReadOf y)
|
||||||
-------------------------- (copy-to-sink)
|
-------------------------- (copy-to-sink)
|
||||||
(let tmp; `=`(tmp, y);
|
(let tmp; `=copy`(tmp, y);
|
||||||
f_sink(tmp))
|
f_sink(tmp))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
246
tests/arc/tcaseobjcopy.nim
Normal file
246
tests/arc/tcaseobjcopy.nim
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
discard """
|
||||||
|
valgrind: true
|
||||||
|
cmd: "nim c --gc:arc -d:useMalloc $file"
|
||||||
|
output: '''myobj destroyed
|
||||||
|
myobj destroyed
|
||||||
|
myobj destroyed
|
||||||
|
A
|
||||||
|
B
|
||||||
|
begin
|
||||||
|
end
|
||||||
|
prevented
|
||||||
|
(ok: true, value: "ok")
|
||||||
|
myobj destroyed
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #13102
|
||||||
|
|
||||||
|
type
|
||||||
|
D = ref object
|
||||||
|
R = object
|
||||||
|
case o: bool
|
||||||
|
of false:
|
||||||
|
discard
|
||||||
|
of true:
|
||||||
|
field: D
|
||||||
|
|
||||||
|
iterator things(): R =
|
||||||
|
when true:
|
||||||
|
var
|
||||||
|
unit = D()
|
||||||
|
while true:
|
||||||
|
yield R(o: true, field: unit)
|
||||||
|
else:
|
||||||
|
while true:
|
||||||
|
var
|
||||||
|
unit = D()
|
||||||
|
yield R(o: true, field: unit)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var i = 0
|
||||||
|
for item in things():
|
||||||
|
discard item.field
|
||||||
|
inc i
|
||||||
|
if i == 2: break
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
# bug #13149
|
||||||
|
|
||||||
|
type
|
||||||
|
TMyObj = object
|
||||||
|
p: pointer
|
||||||
|
len: int
|
||||||
|
|
||||||
|
proc `=destroy`(o: var TMyObj) =
|
||||||
|
if o.p != nil:
|
||||||
|
dealloc o.p
|
||||||
|
o.p = nil
|
||||||
|
echo "myobj destroyed"
|
||||||
|
|
||||||
|
proc `=copy`(dst: var TMyObj, src: TMyObj) =
|
||||||
|
`=destroy`(dst)
|
||||||
|
dst.p = alloc(src.len)
|
||||||
|
dst.len = src.len
|
||||||
|
|
||||||
|
proc `=sink`(dst: var TMyObj, src: TMyObj) =
|
||||||
|
`=destroy`(dst)
|
||||||
|
dst.p = src.p
|
||||||
|
dst.len = src.len
|
||||||
|
|
||||||
|
type
|
||||||
|
TObjKind = enum Z, A, B
|
||||||
|
TCaseObj = object
|
||||||
|
case kind: TObjKind
|
||||||
|
of Z: discard
|
||||||
|
of A:
|
||||||
|
x1: int # this int plays important role
|
||||||
|
x2: TMyObj
|
||||||
|
of B:
|
||||||
|
y: TMyObj
|
||||||
|
|
||||||
|
proc testSinks: TCaseObj =
|
||||||
|
result = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
|
||||||
|
result = TCaseObj(kind: B, y: TMyObj(len: 3, p: alloc(3)))
|
||||||
|
|
||||||
|
proc use(x: TCaseObj) = discard
|
||||||
|
|
||||||
|
proc testCopies(i: int) =
|
||||||
|
var a: array[2, TCaseObj]
|
||||||
|
a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
|
||||||
|
a[i+1] = a[i] # copy, cannot move
|
||||||
|
use(a[i])
|
||||||
|
|
||||||
|
let x1 = testSinks()
|
||||||
|
testCopies(0)
|
||||||
|
|
||||||
|
# bug #12957
|
||||||
|
|
||||||
|
type
|
||||||
|
PegKind* = enum
|
||||||
|
pkCharChoice,
|
||||||
|
pkSequence
|
||||||
|
Peg* = object ## type that represents a PEG
|
||||||
|
case kind: PegKind
|
||||||
|
of pkCharChoice: charChoice: ref set[char]
|
||||||
|
else: discard
|
||||||
|
sons: seq[Peg]
|
||||||
|
|
||||||
|
proc charSet*(s: set[char]): Peg =
|
||||||
|
## constructs a PEG from a character set `s`
|
||||||
|
result = Peg(kind: pkCharChoice)
|
||||||
|
new(result.charChoice)
|
||||||
|
result.charChoice[] = s
|
||||||
|
|
||||||
|
proc len(a: Peg): int {.inline.} = return a.sons.len
|
||||||
|
proc myadd(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
|
||||||
|
|
||||||
|
proc sequence*(a: openArray[Peg]): Peg =
|
||||||
|
result = Peg(kind: pkSequence, sons: @[])
|
||||||
|
when false:
|
||||||
|
#works too:
|
||||||
|
result.myadd(a[0])
|
||||||
|
result.myadd(a[1])
|
||||||
|
for x in items(a):
|
||||||
|
# works:
|
||||||
|
#result.sons.add(x)
|
||||||
|
# fails:
|
||||||
|
result.myadd x
|
||||||
|
if result.len == 1:
|
||||||
|
result = result.sons[0] # this must not move!
|
||||||
|
|
||||||
|
when true:
|
||||||
|
# bug #12957
|
||||||
|
|
||||||
|
proc p =
|
||||||
|
echo "A"
|
||||||
|
let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'}),
|
||||||
|
charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})])
|
||||||
|
echo "B"
|
||||||
|
p()
|
||||||
|
|
||||||
|
proc testSubObjAssignment =
|
||||||
|
echo "begin"
|
||||||
|
# There must be extactly one element in the array constructor!
|
||||||
|
let x = sequence([charSet({'a'..'z', 'A'..'Z', '_'})])
|
||||||
|
echo "end"
|
||||||
|
testSubObjAssignment()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
|
||||||
|
type
|
||||||
|
MyObject = object
|
||||||
|
x1: string
|
||||||
|
case kind1: bool
|
||||||
|
of false: y1: string
|
||||||
|
of true:
|
||||||
|
y2: seq[string]
|
||||||
|
case kind2: bool
|
||||||
|
of true: z1: string
|
||||||
|
of false:
|
||||||
|
z2: seq[string]
|
||||||
|
flag: bool
|
||||||
|
x2: string
|
||||||
|
|
||||||
|
proc test_myobject =
|
||||||
|
var x: MyObject
|
||||||
|
x.x1 = "x1"
|
||||||
|
x.x2 = "x2"
|
||||||
|
x.y1 = "ljhkjhkjh"
|
||||||
|
x.kind1 = true
|
||||||
|
x.y2 = @["1", "2"]
|
||||||
|
x.kind2 = true
|
||||||
|
x.z1 = "yes"
|
||||||
|
x.kind2 = false
|
||||||
|
x.z2 = @["1", "2"]
|
||||||
|
x.kind2 = true
|
||||||
|
x.z1 = "yes"
|
||||||
|
x.kind2 = true # should be no effect
|
||||||
|
doAssert(x.z1 == "yes")
|
||||||
|
x.kind2 = false
|
||||||
|
x.kind1 = x.kind2 # support self assignment with effect
|
||||||
|
|
||||||
|
try:
|
||||||
|
x.kind1 = x.flag # flag is not accesible
|
||||||
|
except FieldDefect:
|
||||||
|
echo "prevented"
|
||||||
|
|
||||||
|
doAssert(x.x1 == "x1")
|
||||||
|
doAssert(x.x2 == "x2")
|
||||||
|
|
||||||
|
|
||||||
|
test_myobject()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
# bug #14244
|
||||||
|
|
||||||
|
type
|
||||||
|
RocksDBResult*[T] = object
|
||||||
|
case ok*: bool
|
||||||
|
of true:
|
||||||
|
value*: T
|
||||||
|
else:
|
||||||
|
error*: string
|
||||||
|
|
||||||
|
proc init(): RocksDBResult[string] =
|
||||||
|
result.ok = true
|
||||||
|
result.value = "ok"
|
||||||
|
|
||||||
|
echo init()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
# bug #14312
|
||||||
|
|
||||||
|
type MyObj = object
|
||||||
|
case kind: bool
|
||||||
|
of false: x0: int # would work with a type like seq[int]; value would be reset
|
||||||
|
of true: x1: string
|
||||||
|
|
||||||
|
var a = MyObj(kind: false, x0: 1234)
|
||||||
|
a.kind = true
|
||||||
|
doAssert(a.x1 == "")
|
||||||
|
|
||||||
|
block:
|
||||||
|
# bug #15532
|
||||||
|
type Kind = enum
|
||||||
|
k0, k1
|
||||||
|
|
||||||
|
type Foo = object
|
||||||
|
y: int
|
||||||
|
case kind: Kind
|
||||||
|
of k0: x0: int
|
||||||
|
of k1: x1: int
|
||||||
|
|
||||||
|
const j0 = Foo(y: 1, kind: k0, x0: 2)
|
||||||
|
const j1 = Foo(y: 1, kind: k1, x1: 2)
|
||||||
|
|
||||||
|
doAssert j0.y == 1
|
||||||
|
doAssert j0.kind == k0
|
||||||
|
doAssert j1.kind == k1
|
||||||
|
|
||||||
|
doAssert j1.x1 == 2
|
||||||
|
doAssert j0.x0 == 2
|
||||||
41
tests/arc/tcomputedgotocopy.nim
Normal file
41
tests/arc/tcomputedgotocopy.nim
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
discard """
|
||||||
|
cmd: '''nim c --newruntime $file'''
|
||||||
|
output: '''2
|
||||||
|
2'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
ObjWithDestructor = object
|
||||||
|
a: int
|
||||||
|
proc `=destroy`(self: var ObjWithDestructor) =
|
||||||
|
echo "destroyed"
|
||||||
|
|
||||||
|
proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
|
||||||
|
echo "copied"
|
||||||
|
|
||||||
|
proc test(a: range[0..1], arg: ObjWithDestructor) =
|
||||||
|
var iteration = 0
|
||||||
|
while true:
|
||||||
|
{.computedGoto.}
|
||||||
|
|
||||||
|
let
|
||||||
|
b = int(a) * 2
|
||||||
|
c = a
|
||||||
|
d = arg
|
||||||
|
e = arg
|
||||||
|
|
||||||
|
discard c
|
||||||
|
discard d
|
||||||
|
discard e
|
||||||
|
|
||||||
|
inc iteration
|
||||||
|
|
||||||
|
case a
|
||||||
|
of 0:
|
||||||
|
assert false
|
||||||
|
of 1:
|
||||||
|
echo b
|
||||||
|
if iteration == 2:
|
||||||
|
break
|
||||||
|
|
||||||
|
test(1, ObjWithDestructor())
|
||||||
526
tests/arc/tmovebugcopy.nim
Normal file
526
tests/arc/tmovebugcopy.nim
Normal file
|
|
@ -0,0 +1,526 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --gc:arc $file"
|
||||||
|
output: '''5
|
||||||
|
(w: 5)
|
||||||
|
(w: -5)
|
||||||
|
c.text = hello
|
||||||
|
c.text = hello
|
||||||
|
p.text = hello
|
||||||
|
p.toks = @["hello"]
|
||||||
|
c.text = hello
|
||||||
|
c[].text = hello
|
||||||
|
pA.text = hello
|
||||||
|
pA.toks = @["hello"]
|
||||||
|
c.text = hello
|
||||||
|
c.text = hello
|
||||||
|
pD.text = hello
|
||||||
|
pD.toks = @["hello"]
|
||||||
|
c.text = hello
|
||||||
|
c.text = hello
|
||||||
|
pOD.text = hello
|
||||||
|
pOD.toks = @["hello"]
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
2
|
||||||
|
mmm
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
fff
|
||||||
|
3
|
||||||
|
mmm
|
||||||
|
sink me (sink)
|
||||||
|
assign me (not sink)
|
||||||
|
sink me (not sink)
|
||||||
|
sinked and not optimized to a bitcopy
|
||||||
|
sinked and not optimized to a bitcopy
|
||||||
|
sinked and not optimized to a bitcopy
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
(data: @[0, 0])
|
||||||
|
100
|
||||||
|
hey
|
||||||
|
hey
|
||||||
|
(a: "a", b: 2)
|
||||||
|
ho
|
||||||
|
(a: "b", b: 3)
|
||||||
|
(b: "b", a: 2)
|
||||||
|
ho
|
||||||
|
(b: "a", a: 3)
|
||||||
|
hey
|
||||||
|
break
|
||||||
|
break
|
||||||
|
hey
|
||||||
|
ho
|
||||||
|
hey
|
||||||
|
ho
|
||||||
|
ho
|
||||||
|
king
|
||||||
|
live long; long live
|
||||||
|
king
|
||||||
|
hi
|
||||||
|
try
|
||||||
|
bye
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# move bug
|
||||||
|
type
|
||||||
|
TMyObj = object
|
||||||
|
p: pointer
|
||||||
|
len: int
|
||||||
|
|
||||||
|
var destroyCounter = 0
|
||||||
|
|
||||||
|
proc `=destroy`(o: var TMyObj) =
|
||||||
|
if o.p != nil:
|
||||||
|
dealloc o.p
|
||||||
|
o.p = nil
|
||||||
|
inc destroyCounter
|
||||||
|
|
||||||
|
proc `=copy`(dst: var TMyObj, src: TMyObj) =
|
||||||
|
`=destroy`(dst)
|
||||||
|
dst.p = alloc(src.len)
|
||||||
|
dst.len = src.len
|
||||||
|
|
||||||
|
proc `=sink`(dst: var TMyObj, src: TMyObj) =
|
||||||
|
`=destroy`(dst)
|
||||||
|
dst.p = src.p
|
||||||
|
dst.len = src.len
|
||||||
|
|
||||||
|
type
|
||||||
|
TObjKind = enum Z, A, B
|
||||||
|
TCaseObj = object
|
||||||
|
case kind: TObjKind
|
||||||
|
of Z: discard
|
||||||
|
of A:
|
||||||
|
x1: int # this int plays important role
|
||||||
|
x2: TMyObj
|
||||||
|
of B:
|
||||||
|
y: TMyObj
|
||||||
|
|
||||||
|
proc use(a: TCaseObj) = discard
|
||||||
|
|
||||||
|
proc moveBug(i: var int) =
|
||||||
|
var a: array[2, TCaseObj]
|
||||||
|
a[i] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 1
|
||||||
|
a[i+1] = a[i] # 2
|
||||||
|
inc i
|
||||||
|
use(a[i-1])
|
||||||
|
|
||||||
|
var x = 0
|
||||||
|
moveBug(x)
|
||||||
|
|
||||||
|
proc moveBug2(): (TCaseObj, TCaseObj) =
|
||||||
|
var a: array[2, TCaseObj]
|
||||||
|
a[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5)))
|
||||||
|
a[1] = a[0] # can move 3
|
||||||
|
result[0] = TCaseObj(kind: A, x1: 5000, x2: TMyObj(len: 5, p: alloc(5))) # 4
|
||||||
|
result[1] = result[0] # 5
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
discard moveBug2()
|
||||||
|
|
||||||
|
main()
|
||||||
|
echo destroyCounter
|
||||||
|
|
||||||
|
# bug #13314
|
||||||
|
|
||||||
|
type
|
||||||
|
O = object
|
||||||
|
v: int
|
||||||
|
R = ref object
|
||||||
|
w: int
|
||||||
|
|
||||||
|
proc `$`(r: R): string = $r[]
|
||||||
|
|
||||||
|
proc tbug13314 =
|
||||||
|
var t5 = R(w: 5)
|
||||||
|
var execute = proc () =
|
||||||
|
echo t5
|
||||||
|
|
||||||
|
execute()
|
||||||
|
t5.w = -5
|
||||||
|
execute()
|
||||||
|
|
||||||
|
tbug13314()
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
# bug #13368
|
||||||
|
|
||||||
|
import strutils
|
||||||
|
proc procStat() =
|
||||||
|
for line in @["a b", "c d", "e f"]:
|
||||||
|
let cols = line.splitWhitespace(maxSplit=1)
|
||||||
|
let x = cols[0]
|
||||||
|
let (nm, rest) = (cols[0], cols[1])
|
||||||
|
procStat()
|
||||||
|
|
||||||
|
|
||||||
|
# bug #14269
|
||||||
|
|
||||||
|
import sugar, strutils
|
||||||
|
|
||||||
|
type
|
||||||
|
Cursor = object
|
||||||
|
text: string
|
||||||
|
Parsed = object
|
||||||
|
text: string
|
||||||
|
toks: seq[string]
|
||||||
|
|
||||||
|
proc tokenize(c: var Cursor): seq[string] =
|
||||||
|
dump c.text
|
||||||
|
return c.text.splitWhitespace()
|
||||||
|
|
||||||
|
proc parse(): Parsed =
|
||||||
|
var c = Cursor(text: "hello")
|
||||||
|
dump c.text
|
||||||
|
return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text
|
||||||
|
|
||||||
|
let p = parse()
|
||||||
|
dump p.text
|
||||||
|
dump p.toks
|
||||||
|
|
||||||
|
|
||||||
|
proc tokenizeA(c: ptr Cursor): seq[string] =
|
||||||
|
dump c[].text
|
||||||
|
return c[].text.splitWhitespace()
|
||||||
|
|
||||||
|
proc parseA(): Parsed =
|
||||||
|
var c = Cursor(text: "hello")
|
||||||
|
dump c.text
|
||||||
|
return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text
|
||||||
|
|
||||||
|
let pA = parseA()
|
||||||
|
dump pA.text
|
||||||
|
dump pA.toks
|
||||||
|
|
||||||
|
|
||||||
|
proc tokenizeD(c: Cursor): seq[string] =
|
||||||
|
dump c.text
|
||||||
|
return c.text.splitWhitespace()
|
||||||
|
|
||||||
|
proc parseD(): Parsed =
|
||||||
|
var c = cast[ptr Cursor](alloc0(sizeof(Cursor)))
|
||||||
|
c[] = Cursor(text: "hello")
|
||||||
|
dump c.text
|
||||||
|
return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text
|
||||||
|
|
||||||
|
let pD = parseD()
|
||||||
|
dump pD.text
|
||||||
|
dump pD.toks
|
||||||
|
|
||||||
|
# Bug would only pop up with owned refs
|
||||||
|
proc tokenizeOD(c: Cursor): seq[string] =
|
||||||
|
dump c.text
|
||||||
|
return c.text.splitWhitespace()
|
||||||
|
|
||||||
|
proc parseOD(): Parsed =
|
||||||
|
var c = new Cursor
|
||||||
|
c[] = Cursor(text: "hello")
|
||||||
|
dump c.text
|
||||||
|
return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text
|
||||||
|
|
||||||
|
let pOD = parseOD()
|
||||||
|
dump pOD.text
|
||||||
|
dump pOD.toks
|
||||||
|
|
||||||
|
when false:
|
||||||
|
# Bug would only pop up with owned refs and implicit derefs, but since they don't work together..
|
||||||
|
{.experimental: "implicitDeref".}
|
||||||
|
proc tokenizeOHD(c: Cursor): seq[string] =
|
||||||
|
dump c.text
|
||||||
|
return c.text.splitWhitespace()
|
||||||
|
|
||||||
|
proc parseOHD(): Parsed =
|
||||||
|
var c = new Cursor
|
||||||
|
c[] = Cursor(text: "hello")
|
||||||
|
dump c.text
|
||||||
|
return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text
|
||||||
|
|
||||||
|
let pOHD = parseOHD()
|
||||||
|
dump pOHD.text
|
||||||
|
dump pOHD.toks
|
||||||
|
|
||||||
|
# bug #13456
|
||||||
|
|
||||||
|
iterator combinations[T](s: openarray[T], k: int): seq[T] =
|
||||||
|
let n = len(s)
|
||||||
|
assert k >= 0 and k <= n
|
||||||
|
var pos = newSeq[int](k)
|
||||||
|
var current = newSeq[T](k)
|
||||||
|
for i in 0..k-1:
|
||||||
|
pos[k-i-1] = i
|
||||||
|
var done = false
|
||||||
|
while not done:
|
||||||
|
for i in 0..k-1:
|
||||||
|
current[i] = s[pos[k-i-1]]
|
||||||
|
yield current
|
||||||
|
var i = 0
|
||||||
|
while i < k:
|
||||||
|
pos[i] += 1
|
||||||
|
if pos[i] < n-i:
|
||||||
|
for j in 0..i-1:
|
||||||
|
pos[j] = pos[i] + i - j
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
if i >= k:
|
||||||
|
break
|
||||||
|
|
||||||
|
type
|
||||||
|
UndefEx = object of ValueError
|
||||||
|
|
||||||
|
proc main2 =
|
||||||
|
var delayedSyms = @[1, 2, 3]
|
||||||
|
var unp: seq[int]
|
||||||
|
block myb:
|
||||||
|
for a in 1 .. 2:
|
||||||
|
if delayedSyms.len > a:
|
||||||
|
unp = delayedSyms
|
||||||
|
for t in unp.combinations(a + 1):
|
||||||
|
try:
|
||||||
|
var h = false
|
||||||
|
for k in t:
|
||||||
|
echo "fff"
|
||||||
|
if h: continue
|
||||||
|
if true:
|
||||||
|
raise newException(UndefEx, "forward declaration")
|
||||||
|
break myb
|
||||||
|
except UndefEx:
|
||||||
|
echo t.len
|
||||||
|
echo "mmm"
|
||||||
|
|
||||||
|
main2()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type ME = object
|
||||||
|
who: string
|
||||||
|
|
||||||
|
proc `=copy`(x: var ME, y: ME) =
|
||||||
|
if y.who.len > 0: echo "assign ",y.who
|
||||||
|
|
||||||
|
proc `=sink`(x: var ME, y: ME) =
|
||||||
|
if y.who.len > 0: echo "sink ",y.who
|
||||||
|
|
||||||
|
var dump: ME
|
||||||
|
template use(x) = dump = x
|
||||||
|
template def(x) = x = dump
|
||||||
|
|
||||||
|
var c = true
|
||||||
|
|
||||||
|
proc shouldSink() =
|
||||||
|
var x = ME(who: "me (sink)")
|
||||||
|
use(x) # we analyse this
|
||||||
|
if c: def(x)
|
||||||
|
else: def(x)
|
||||||
|
use(x) # ok, with the [else] part.
|
||||||
|
|
||||||
|
shouldSink()
|
||||||
|
|
||||||
|
dump = ME()
|
||||||
|
|
||||||
|
proc shouldNotSink() =
|
||||||
|
var x = ME(who: "me (not sink)")
|
||||||
|
use(x) # we analyse this
|
||||||
|
if c: def(x)
|
||||||
|
use(x) # Not ok without the '[else]'
|
||||||
|
|
||||||
|
shouldNotSink()
|
||||||
|
|
||||||
|
# bug #14568
|
||||||
|
import os
|
||||||
|
|
||||||
|
type O2 = object
|
||||||
|
s: seq[int]
|
||||||
|
|
||||||
|
proc `=sink`(dest: var O2, src: O2) =
|
||||||
|
echo "sinked and not optimized to a bitcopy"
|
||||||
|
|
||||||
|
var testSeq: O2
|
||||||
|
|
||||||
|
proc update() =
|
||||||
|
# testSeq.add(0) # uncommenting this line fixes the leak
|
||||||
|
testSeq = O2(s: @[])
|
||||||
|
testSeq.s.add(0)
|
||||||
|
|
||||||
|
for i in 1..3:
|
||||||
|
update()
|
||||||
|
|
||||||
|
|
||||||
|
# bug #14961
|
||||||
|
type
|
||||||
|
Foo = object
|
||||||
|
data: seq[int]
|
||||||
|
|
||||||
|
proc initFoo(len: int): Foo =
|
||||||
|
result = (let s = newSeq[int](len); Foo(data: s) )
|
||||||
|
|
||||||
|
var f = initFoo(2)
|
||||||
|
echo initFoo(2)
|
||||||
|
|
||||||
|
proc initFoo2(len: int) =
|
||||||
|
echo if true:
|
||||||
|
let s = newSeq[int](len); Foo(data: s)
|
||||||
|
else:
|
||||||
|
let s = newSeq[int](len); Foo(data: s)
|
||||||
|
|
||||||
|
initFoo2(2)
|
||||||
|
|
||||||
|
proc initFoo3(len: int) =
|
||||||
|
echo (block:
|
||||||
|
let s = newSeq[int](len); Foo(data: s))
|
||||||
|
|
||||||
|
initFoo3(2)
|
||||||
|
|
||||||
|
proc initFoo4(len: int) =
|
||||||
|
echo (let s = newSeq[int](len); Foo(data: s))
|
||||||
|
|
||||||
|
initFoo4(2)
|
||||||
|
|
||||||
|
proc initFoo5(len: int) =
|
||||||
|
echo (case true
|
||||||
|
of true:
|
||||||
|
let s = newSeq[int](len); Foo(data: s)
|
||||||
|
of false:
|
||||||
|
let s = newSeq[int](len); Foo(data: s))
|
||||||
|
|
||||||
|
initFoo5(2)
|
||||||
|
|
||||||
|
proc initFoo6(len: int) =
|
||||||
|
echo (block:
|
||||||
|
try:
|
||||||
|
let s = newSeq[int](len); Foo(data: s)
|
||||||
|
finally: discard)
|
||||||
|
|
||||||
|
initFoo6(2)
|
||||||
|
|
||||||
|
proc initFoo7(len: int) =
|
||||||
|
echo (block:
|
||||||
|
try:
|
||||||
|
raise newException(CatchableError, "sup")
|
||||||
|
let s = newSeq[int](len); Foo(data: s)
|
||||||
|
except CatchableError:
|
||||||
|
let s = newSeq[int](len); Foo(data: s) )
|
||||||
|
|
||||||
|
initFoo7(2)
|
||||||
|
|
||||||
|
|
||||||
|
# bug #14902
|
||||||
|
iterator zip[T](s: openarray[T]): (T, T) =
|
||||||
|
var i = 0
|
||||||
|
while i < 10:
|
||||||
|
yield (s[i mod 2], s[i mod 2 + 1])
|
||||||
|
inc i
|
||||||
|
|
||||||
|
var lastMem = int.high
|
||||||
|
|
||||||
|
proc leak =
|
||||||
|
const len = 10
|
||||||
|
var x = @[newString(len), newString(len), newString(len)]
|
||||||
|
|
||||||
|
var c = 0
|
||||||
|
for (a, b) in zip(x):
|
||||||
|
let newMem = getOccupiedMem()
|
||||||
|
assert newMem <= lastMem
|
||||||
|
lastMem = newMem
|
||||||
|
c += a.len
|
||||||
|
echo c
|
||||||
|
|
||||||
|
leak()
|
||||||
|
|
||||||
|
|
||||||
|
proc consume(a: sink string) = echo a
|
||||||
|
|
||||||
|
proc weirdScopes =
|
||||||
|
if (let a = "hey"; a.len > 0):
|
||||||
|
echo a
|
||||||
|
|
||||||
|
while (let a = "hey"; a.len > 0):
|
||||||
|
echo a
|
||||||
|
break
|
||||||
|
|
||||||
|
var a = block: (a: "a", b: 2)
|
||||||
|
echo a
|
||||||
|
(discard; a) = (echo "ho"; (a: "b", b: 3))
|
||||||
|
echo a
|
||||||
|
|
||||||
|
var b = try: (b: "b", a: 2)
|
||||||
|
except: raise
|
||||||
|
echo b
|
||||||
|
(discard; b) = (echo "ho"; (b: "a", a: 3))
|
||||||
|
echo b
|
||||||
|
|
||||||
|
var s = "break"
|
||||||
|
consume((echo "hey"; s))
|
||||||
|
echo s
|
||||||
|
|
||||||
|
echo (block:
|
||||||
|
var a = "hey"
|
||||||
|
(echo "hey"; "ho"))
|
||||||
|
|
||||||
|
var b2 = "ho"
|
||||||
|
echo (block:
|
||||||
|
var a = "hey"
|
||||||
|
(echo "hey"; b2))
|
||||||
|
echo b2
|
||||||
|
|
||||||
|
type status = enum
|
||||||
|
alive
|
||||||
|
|
||||||
|
var king = "king"
|
||||||
|
echo (block:
|
||||||
|
var a = "a"
|
||||||
|
when true:
|
||||||
|
var b = "b"
|
||||||
|
case alive
|
||||||
|
of alive:
|
||||||
|
try:
|
||||||
|
var c = "c"
|
||||||
|
if true:
|
||||||
|
king
|
||||||
|
else:
|
||||||
|
"the abyss"
|
||||||
|
except:
|
||||||
|
echo "he ded"
|
||||||
|
"dead king")
|
||||||
|
echo "live long; long live"
|
||||||
|
echo king
|
||||||
|
|
||||||
|
weirdScopes()
|
||||||
|
|
||||||
|
|
||||||
|
# bug #14985
|
||||||
|
proc getScope(): string =
|
||||||
|
if true:
|
||||||
|
"hi"
|
||||||
|
else:
|
||||||
|
"else"
|
||||||
|
|
||||||
|
echo getScope()
|
||||||
|
|
||||||
|
proc getScope3(): string =
|
||||||
|
try:
|
||||||
|
"try"
|
||||||
|
except:
|
||||||
|
"except"
|
||||||
|
|
||||||
|
echo getScope3()
|
||||||
|
|
||||||
|
proc getScope2(): string =
|
||||||
|
case true
|
||||||
|
of true:
|
||||||
|
"bye"
|
||||||
|
else:
|
||||||
|
"else"
|
||||||
|
|
||||||
|
echo getScope2()
|
||||||
|
|
@ -33,9 +33,9 @@ result = (
|
||||||
var
|
var
|
||||||
sibling
|
sibling
|
||||||
saved
|
saved
|
||||||
`=`(sibling, target.parent.left)
|
`=copy`(sibling, target.parent.left)
|
||||||
`=`(saved, sibling.right)
|
`=copy`(saved, sibling.right)
|
||||||
`=`(sibling.right, saved.left)
|
`=copy`(sibling.right, saved.left)
|
||||||
`=sink`(sibling.parent, saved)
|
`=sink`(sibling.parent, saved)
|
||||||
`=destroy`(sibling)
|
`=destroy`(sibling)
|
||||||
-- end of expandArc ------------------------
|
-- end of expandArc ------------------------
|
||||||
|
|
@ -46,7 +46,7 @@ var
|
||||||
lvalue
|
lvalue
|
||||||
lnext
|
lnext
|
||||||
_
|
_
|
||||||
`=`(lresult, [123])
|
`=copy`(lresult, [123])
|
||||||
_ = (
|
_ = (
|
||||||
let blitTmp = lresult
|
let blitTmp = lresult
|
||||||
blitTmp, ";")
|
blitTmp, ";")
|
||||||
|
|
@ -67,10 +67,10 @@ try:
|
||||||
var it_cursor = x
|
var it_cursor = x
|
||||||
a = (
|
a = (
|
||||||
wasMoved(:tmpD)
|
wasMoved(:tmpD)
|
||||||
`=`(:tmpD, it_cursor.key)
|
`=copy`(:tmpD, it_cursor.key)
|
||||||
:tmpD,
|
:tmpD,
|
||||||
wasMoved(:tmpD_1)
|
wasMoved(:tmpD_1)
|
||||||
`=`(:tmpD_1, it_cursor.val)
|
`=copy`(:tmpD_1, it_cursor.val)
|
||||||
:tmpD_1)
|
:tmpD_1)
|
||||||
echo [
|
echo [
|
||||||
:tmpD_2 = `$`(a)
|
:tmpD_2 = `$`(a)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ try:
|
||||||
return
|
return
|
||||||
add(a):
|
add(a):
|
||||||
wasMoved(:tmpD)
|
wasMoved(:tmpD)
|
||||||
`=`(:tmpD, x)
|
`=copy`(:tmpD, x)
|
||||||
:tmpD
|
:tmpD
|
||||||
inc i_1, 1
|
inc i_1, 1
|
||||||
if cond:
|
if cond:
|
||||||
|
|
|
||||||
154
tests/arc/tweavecopy.nim
Normal file
154
tests/arc/tweavecopy.nim
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
discard """
|
||||||
|
outputsub: '''Success'''
|
||||||
|
cmd: '''nim c --gc:arc --threads:on $file'''
|
||||||
|
disabled: "bsd"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #13936
|
||||||
|
|
||||||
|
import std/atomics
|
||||||
|
|
||||||
|
const MemBlockSize = 256
|
||||||
|
|
||||||
|
type
|
||||||
|
ChannelSPSCSingle* = object
|
||||||
|
full{.align: 128.}: Atomic[bool]
|
||||||
|
itemSize*: uint8
|
||||||
|
buffer*{.align: 8.}: UncheckedArray[byte]
|
||||||
|
|
||||||
|
proc `=copy`(
|
||||||
|
dest: var ChannelSPSCSingle,
|
||||||
|
source: ChannelSPSCSingle
|
||||||
|
) {.error: "A channel cannot be copied".}
|
||||||
|
|
||||||
|
proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
|
||||||
|
## If ChannelSPSCSingle is used intrusive another data structure
|
||||||
|
## be aware that it should be the last part due to ending by UncheckedArray
|
||||||
|
## Also due to 128 bytes padding, it automatically takes half
|
||||||
|
## of the default MemBlockSize
|
||||||
|
assert itemsize.int in 0 .. int high(uint8)
|
||||||
|
assert itemSize.int +
|
||||||
|
sizeof(chan.itemsize) +
|
||||||
|
sizeof(chan.full) < MemBlockSize
|
||||||
|
|
||||||
|
chan.itemSize = uint8 itemsize
|
||||||
|
chan.full.store(false, moRelaxed)
|
||||||
|
|
||||||
|
func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
|
||||||
|
not chan.full.load(moAcquire)
|
||||||
|
|
||||||
|
func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
|
||||||
|
## Try receiving the item buffered in the channel
|
||||||
|
## Returns true if successful (channel was not empty)
|
||||||
|
##
|
||||||
|
## ⚠ Use only in the consumer thread that reads from the channel.
|
||||||
|
assert (sizeof(T) == chan.itemsize.int) or
|
||||||
|
# Support dummy object
|
||||||
|
(sizeof(T) == 0 and chan.itemsize == 1)
|
||||||
|
|
||||||
|
let full = chan.full.load(moAcquire)
|
||||||
|
if not full:
|
||||||
|
return false
|
||||||
|
dst = cast[ptr T](chan.buffer.addr)[]
|
||||||
|
chan.full.store(false, moRelease)
|
||||||
|
return true
|
||||||
|
|
||||||
|
func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
|
||||||
|
## Try sending an item into the channel
|
||||||
|
## Reurns true if successful (channel was empty)
|
||||||
|
##
|
||||||
|
## ⚠ Use only in the producer thread that writes from the channel.
|
||||||
|
assert (sizeof(T) == chan.itemsize.int) or
|
||||||
|
# Support dummy object
|
||||||
|
(sizeof(T) == 0 and chan.itemsize == 1)
|
||||||
|
|
||||||
|
let full = chan.full.load(moAcquire)
|
||||||
|
if full:
|
||||||
|
return false
|
||||||
|
cast[ptr T](chan.buffer.addr)[] = src
|
||||||
|
chan.full.store(true, moRelease)
|
||||||
|
return true
|
||||||
|
|
||||||
|
# Sanity checks
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
when isMainModule:
|
||||||
|
|
||||||
|
when not compileOption("threads"):
|
||||||
|
{.error: "This requires --threads:on compilation flag".}
|
||||||
|
|
||||||
|
template sendLoop[T](chan: var ChannelSPSCSingle,
|
||||||
|
data: sink T,
|
||||||
|
body: untyped): untyped =
|
||||||
|
while not chan.trySend(data):
|
||||||
|
body
|
||||||
|
|
||||||
|
template recvLoop[T](chan: var ChannelSPSCSingle,
|
||||||
|
data: var T,
|
||||||
|
body: untyped): untyped =
|
||||||
|
while not chan.tryRecv(data):
|
||||||
|
body
|
||||||
|
|
||||||
|
type
|
||||||
|
ThreadArgs = object
|
||||||
|
ID: WorkerKind
|
||||||
|
chan: ptr ChannelSPSCSingle
|
||||||
|
|
||||||
|
WorkerKind = enum
|
||||||
|
Sender
|
||||||
|
Receiver
|
||||||
|
|
||||||
|
template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
|
||||||
|
if args.ID == id:
|
||||||
|
body
|
||||||
|
|
||||||
|
proc thread_func(args: ThreadArgs) =
|
||||||
|
|
||||||
|
# Worker RECEIVER:
|
||||||
|
# ---------
|
||||||
|
# <- chan
|
||||||
|
# <- chan
|
||||||
|
# <- chan
|
||||||
|
#
|
||||||
|
# Worker SENDER:
|
||||||
|
# ---------
|
||||||
|
# chan <- 42
|
||||||
|
# chan <- 53
|
||||||
|
# chan <- 64
|
||||||
|
Worker(Receiver):
|
||||||
|
var val: int
|
||||||
|
for j in 0 ..< 10:
|
||||||
|
args.chan[].recvLoop(val):
|
||||||
|
# Busy loop, in prod we might want to yield the core/thread timeslice
|
||||||
|
discard
|
||||||
|
echo " Receiver got: ", val
|
||||||
|
doAssert val == 42 + j*11
|
||||||
|
|
||||||
|
Worker(Sender):
|
||||||
|
doAssert args.chan.full.load(moRelaxed) == false
|
||||||
|
for j in 0 ..< 10:
|
||||||
|
let val = 42 + j*11
|
||||||
|
args.chan[].sendLoop(val):
|
||||||
|
# Busy loop, in prod we might want to yield the core/thread timeslice
|
||||||
|
discard
|
||||||
|
echo "Sender sent: ", val
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
echo "Testing if 2 threads can send data"
|
||||||
|
echo "-----------------------------------"
|
||||||
|
var threads: array[2, Thread[ThreadArgs]]
|
||||||
|
|
||||||
|
var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
|
||||||
|
chan[].initialize(itemSize = sizeof(int))
|
||||||
|
|
||||||
|
createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
|
||||||
|
createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
|
||||||
|
|
||||||
|
joinThread(threads[0])
|
||||||
|
joinThread(threads[1])
|
||||||
|
|
||||||
|
freeShared(chan)
|
||||||
|
|
||||||
|
echo "-----------------------------------"
|
||||||
|
echo "Success"
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim c --newruntime $file"
|
cmd: "nim c --newruntime $file"
|
||||||
errormsg: "'=' is not available for type <owned Foo>; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice"
|
errormsg: "'=copy' is not available for type <owned Foo>; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice"
|
||||||
line: 11
|
line: 11
|
||||||
"""
|
"""
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
||||||
line: 29
|
line: 29
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
||||||
file: "tprevent_assign2.nim"
|
file: "tprevent_assign2.nim"
|
||||||
line: 48
|
line: 48
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "'=' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
|
||||||
file: "tprevent_assign3.nim"
|
file: "tprevent_assign3.nim"
|
||||||
line: 46
|
line: 46
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: '''nim c --newruntime $file'''
|
cmd: '''nim c --newruntime $file'''
|
||||||
errormsg: "'=' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)"
|
errormsg: "'=copy' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)"
|
||||||
line: 48
|
line: 48
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue