diff options
Diffstat (limited to 'tests/effects')
41 files changed, 1087 insertions, 53 deletions
diff --git a/tests/effects/tcast_as_pragma.nim b/tests/effects/tcast_as_pragma.nim new file mode 100644 index 000000000..1de61333e --- /dev/null +++ b/tests/effects/tcast_as_pragma.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nim c $file" + action: "compile" +""" + +proc taggy() {.tags: RootEffect.} = discard + +proc m {.raises: [], tags: [].} = + {.cast(noSideEffect).}: + echo "hi" + + {.cast(raises: []).}: + raise newException(ValueError, "bah") + + {.cast(tags: []).}: + taggy() + +m() diff --git a/tests/effects/tdiagnostic_messages.nim b/tests/effects/tdiagnostic_messages.nim new file mode 100644 index 000000000..b1acf8c5c --- /dev/null +++ b/tests/effects/tdiagnostic_messages.nim @@ -0,0 +1,37 @@ +discard """ + nimoutFull: true + action: "reject" + cmd: "nim r --hint:Conf:off $file" + nimout: ''' +tdiagnostic_messages.nim(36, 6) Error: 'a' can have side effects +> tdiagnostic_messages.nim(37, 30) Hint: 'a' calls `.sideEffect` 'callWithSideEffects' +>> tdiagnostic_messages.nim(29, 6) Hint: 'callWithSideEffects' called by 'a' +>>> tdiagnostic_messages.nim(31, 34) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaVarParam' +>>>> tdiagnostic_messages.nim(25, 6) Hint: 'indirectCallViaVarParam' called by 'callWithSideEffects' +>>>>> tdiagnostic_messages.nim(26, 7) Hint: 'indirectCallViaVarParam' calls routine via hidden pointer indirection +>>> tdiagnostic_messages.nim(32, 33) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaPointer' +>>>> tdiagnostic_messages.nim(27, 6) Hint: 'indirectCallViaPointer' called by 'callWithSideEffects' +>>>>> tdiagnostic_messages.nim(28, 32) Hint: 'indirectCallViaPointer' calls routine via pointer indirection +>>> tdiagnostic_messages.nim(33, 3) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho' +>>>> tdiagnostic_messages.nim(24, 6) Hint: 'myEcho' called by 'callWithSideEffects' +>>> tdiagnostic_messages.nim(34, 3) Hint: 'callWithSideEffects' accesses global state 'globalVar' +>>>> tdiagnostic_messages.nim(23, 5) Hint: 'globalVar' accessed by 'callWithSideEffects' + +''' +""" + +var globalVar = 0 +proc myEcho(a: string) {.sideEffect.} = discard +proc indirectCallViaVarParam(call: var proc(): int {.nimcall.}): int = + call() +proc indirectCallViaPointer(call: pointer): int = + cast[ptr proc(): int](call)[]() +proc callWithSideEffects(): int = + var p = proc (): int {.nimcall.} = 0 + discard indirectCallViaVarParam(p) + discard indirectCallViaPointer(addr p) + myEcho "" + globalVar + +func a: int = + discard callWithSideEffects() diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 3bd19f4ab..1d267b5fa 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,15 +1,10 @@ discard """ - errormsg: "type mismatch: got <proc (x: int): string{.noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}'" - file: "teffects1.nim" - line: 38 - cmd: "nim check $file" - nimout: '''teffects1.nim(22, 28) template/generic instantiation from here -teffects1.nim(23, 13) Error: can raise an unlisted exception: ref IOError -teffects1.nim(22, 29) Hint: 'IO2Error' is declared but not used [XDeclaredButNotUsed] -teffects1.nim(38, 21) Error: type mismatch: got <proc (x: int): string{.noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}' -.raise effects differ''' + cmd: "nim check --hint:Conf:off --hint:XDeclaredButNotUsed:off $file" + nimout: ''' +teffects1.nim(17, 28) template/generic instantiation from here +''' """ - +{.push warningAsError[Effect]: on.} type TObj {.pure, inheritable.} = object TObjB = object of TObj @@ -19,21 +14,34 @@ type proc forw: int {. .} -proc lier(): int {.raises: [IO2Error].} = - writeLine stdout, "arg" +proc lier(): int {.raises: [IO2Error].} = #[tt.Hint + ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]# + writeLine stdout, "arg" #[tt.Error + ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]# proc forw: int = raise newException(IOError, "arg") +block: + proc someProc(t: string) {.raises: [Defect].} = + discard + let vh: proc(topic: string) {.raises: [].} = someProc + {.push raises: [Defect].} type MyProcType* = proc(x: int): string #{.raises: [ValueError, Defect].} -proc foo(x: int): string {.raises: [ValueError].} = +proc foo(x: int): string {.nimcall, raises: [ValueError].} = if x > 9: raise newException(ValueError, "Use single digit") $x -var p: MyProcType = foo +var p: MyProcType = foo #[tt.Error + ^ +type mismatch: got <proc (x: int): string{.nimcall, raises: [ValueError], noSideEffect, gcsafe.}> but expected 'MyProcType = proc (x: int): string{.closure.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. +.raise effects differ +]# +{.pop.} {.pop.} diff --git a/tests/effects/teffects10.nim b/tests/effects/teffects10.nim new file mode 100644 index 000000000..8193b394a --- /dev/null +++ b/tests/effects/teffects10.nim @@ -0,0 +1,12 @@ +discard """ +action: compile +""" + +# https://github.com/nim-lang/Nim/issues/15495 + +proc f() {.raises: [].} = + var a: proc () + var b: proc () + swap(a, b) + +f() diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim new file mode 100644 index 000000000..20b7026ab --- /dev/null +++ b/tests/effects/teffects11.nim @@ -0,0 +1,21 @@ +discard """ +action: compile +errormsg: "type mismatch: got <proc (x: int){.gcsafe.}>" +line: 21 +""" + +type + Effect1 = object + Effect2 = object + Effect3 = object + +proc test(fnc: proc(x: int): void {.forbids: [Effect2].}) {.tags: [Effect1, Effect3, RootEffect].} = + fnc(1) + +proc t1(x: int): void = echo $x +proc t2(x: int): void {.tags: [Effect2].} = echo $x +proc t3(x: int): void {.tags: [Effect3].} = echo $x + +test(t1) +test(t3) +test(t2) diff --git a/tests/effects/teffects12.nim b/tests/effects/teffects12.nim new file mode 100644 index 000000000..5f5499c38 --- /dev/null +++ b/tests/effects/teffects12.nim @@ -0,0 +1,52 @@ +discard """ +action: compile +""" + +import std/locks + +type + Test2Effect* = object + Test2* = object + value2*: int + Test1Effect* = object + Test1* = object + value1*: int + Main* = object + test1Lock: Lock + test1: Test1 + test2Lock: Lock + test2: Test2 + +proc `=copy`(obj1: var Test2, obj2: Test2) {.error.} +proc `=copy`(obj1: var Test1, obj2: Test1) {.error.} +proc `=copy`(obj1: var Main, obj2: Main) {.error.} + +proc withTest1(main: var Main, + fn: proc(test1: var Test1) {.gcsafe, forbids: [Test1Effect].}) {.gcsafe, tags: [Test1Effect, RootEffect].} = + withLock(main.test1Lock): + fn(main.test1) + +proc withTest2(main: var Main, + fn: proc(test1: var Test2) {.gcsafe, forbids: [Test2Effect].}) {.gcsafe, tags: [Test2Effect, RootEffect].} = + withLock(main.test2Lock): + fn(main.test2) + +proc newMain(): Main = + var test1lock: Lock + initLock(test1Lock) + var test2lock: Lock + initLock(test2Lock) + var main = Main(test1Lock: move(test1Lock), test1: Test1(value1: 1), + test2Lock: move(test2Lock), test2: Test2(value2: 2)) + main.withTest1(proc(test1: var Test1) = test1.value1 += 1) + main.withTest2(proc(test2: var Test2) = test2.value2 += 1) + move main + +var main = newMain() +main.withTest1(proc(test1: var Test1) = + test1.value1 += 1 + main.withTest2(proc(test2: var Test2) = test2.value2 += 1) +) + +main.withTest1(proc(test1: var Test1) {.tags: [].} = echo $test1.value1) +main.withTest2(proc(test2: var Test2) {.tags: [].} = echo $test2.value2) diff --git a/tests/effects/teffects13.nim b/tests/effects/teffects13.nim new file mode 100644 index 000000000..73082f997 --- /dev/null +++ b/tests/effects/teffects13.nim @@ -0,0 +1,19 @@ +discard """ +action: compile +errormsg: "writeSomething() has an illegal effect: WriteIO" +line: 19 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(): void {.tags: [WriteIO].} = echo "..." + +proc noWritesPlease() {.forbids: [WriteIO].} = + # this is OK: + echo readSomething() + # the compiler prevents this: + writeSomething() diff --git a/tests/effects/teffects14.nim b/tests/effects/teffects14.nim new file mode 100644 index 000000000..6291d9569 --- /dev/null +++ b/tests/effects/teffects14.nim @@ -0,0 +1,15 @@ +discard """ +action: compile +errormsg: "func1() has an illegal effect: IO" +line: 15 +""" + +type IO = object ## input/output effect +proc func1(): string {.tags: [IO].} = discard +proc func2(): string = discard + +proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + discard func2() + # the compiler prevents this: + let y = func1() diff --git a/tests/effects/teffects15.nim b/tests/effects/teffects15.nim new file mode 100644 index 000000000..c3079cdbc --- /dev/null +++ b/tests/effects/teffects15.nim @@ -0,0 +1,18 @@ +discard """ +action: compile +errormsg: "method1(c) has an illegal effect: IO" +line: 18 +""" + +type + IO = object ## input/output effect + CustomObject* = object of RootObj + text: string + +method method1(obj: var CustomObject): string {.tags: [IO].} = obj.text & "." +method method2(obj: var CustomObject): string = obj.text & ":" + +proc noIO() {.forbids: [IO].} = + var c = CustomObject(text: "a") + echo c.method2() + echo c.method1() diff --git a/tests/effects/teffects16.nim b/tests/effects/teffects16.nim new file mode 100644 index 000000000..ee8f782a3 --- /dev/null +++ b/tests/effects/teffects16.nim @@ -0,0 +1,20 @@ +discard """ +action: compile +errormsg: "writeSomething(\"a\") can have an unlisted effect: WriteIO" +line: 20 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + LogIO = object of IO ## another output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg +proc logSomething(msg: string): void {.tags: [LogIo].} = echo msg + +proc noWritesPlease() {.forbids: [WriteIO], tags: [LogIO, ReadIO].} = + echo readSomething() + logSomething("a") + writeSomething("a") diff --git a/tests/effects/teffects17.nim b/tests/effects/teffects17.nim new file mode 100644 index 000000000..5e6b83896 --- /dev/null +++ b/tests/effects/teffects17.nim @@ -0,0 +1,17 @@ +discard """ +action: compile +errormsg: "writeSomething(\"a\") has an illegal effect: WriteIO" +line: 17 +""" + +type + IO = object of RootEffect ## input/output effect + ReadIO = object of IO ## input effect + WriteIO = object of IO ## output effect + +proc readSomething(): string {.tags: [ReadIO].} = "" +proc writeSomething(msg: string): void {.tags: [WriteIO].} = echo msg + +proc illegalEffectNegation() {.forbids: [WriteIO], tags: [ReadIO, WriteIO].} = + echo readSomething() + writeSomething("a") diff --git a/tests/effects/teffects18.nim b/tests/effects/teffects18.nim new file mode 100644 index 000000000..576e76635 --- /dev/null +++ b/tests/effects/teffects18.nim @@ -0,0 +1,19 @@ +discard """ +action: compile +errormsg: "type mismatch: got <ProcType2>" +line: 19 +""" + +type MyEffect = object +type ProcType1 = proc (i: int): void {.forbids: [MyEffect].} +type ProcType2 = proc (i: int): void + +proc testFunc(p: ProcType1): void = p(1) + +proc toBeCalled(i: int): void {.tags: [MyEffect].} = echo $i + +let emptyTags = proc(i: int): void {.tags: [].} = echo $i +let noTags: ProcType2 = proc(i: int): void = toBeCalled(i) + +testFunc(emptyTags) +testFunc(noTags) diff --git a/tests/effects/teffects19.nim b/tests/effects/teffects19.nim new file mode 100644 index 000000000..6b4ab0819 --- /dev/null +++ b/tests/effects/teffects19.nim @@ -0,0 +1,23 @@ +discard """ +action: compile +errormsg: "type mismatch: got <proc (i: int){.gcsafe.}>" +line: 23 +""" + +type MyEffect = object +type ProcType1 = proc (i: int): void {.forbids: [MyEffect].} +type ProcType2 = proc (i: int): void + +proc caller1(p: ProcType1): void = p(1) +proc caller2(p: ProcType2): void = p(1) + +proc effectful(i: int): void {.tags: [MyEffect].} = echo $i +proc effectless(i: int): void {.forbids: [MyEffect].} = echo $i + +proc toBeCalled1(i: int): void = effectful(i) +proc toBeCalled2(i: int): void = effectless(i) + +caller1(toBeCalled2) +caller2(toBeCalled1) +caller2(toBeCalled2) +caller1(toBeCalled1) diff --git a/tests/effects/teffects2.nim b/tests/effects/teffects2.nim index e4b50aba5..777a4cebc 100644 --- a/tests/effects/teffects2.nim +++ b/tests/effects/teffects2.nim @@ -2,9 +2,9 @@ discard """ errormsg: "can raise an unlisted exception: ref IOError" line: 19 """ - +{.push warningAsError[Effect]: on.} type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string @@ -17,3 +17,4 @@ proc lier(): int {.raises: [IOError].} = proc forw: int = raise newException(IOError, "arg") +{.pop.} diff --git a/tests/effects/teffects3.nim b/tests/effects/teffects3.nim index ee5470c47..4c050510a 100644 --- a/tests/effects/teffects3.nim +++ b/tests/effects/teffects3.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string fn: proc (): int {.tags: [].} diff --git a/tests/effects/teffects4.nim b/tests/effects/teffects4.nim index 88cc0efa9..b875754b6 100644 --- a/tests/effects/teffects4.nim +++ b/tests/effects/teffects4.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj = object {.pure, inheritable.} + TObj {.pure, inheritable.} = object TObjB = object of TObj a, b, c: string fn: proc (): int {.tags: [ReadIOEffect].} diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim index 6a4eea155..d3af22434 100644 --- a/tests/effects/teffects6.nim +++ b/tests/effects/teffects6.nim @@ -21,7 +21,7 @@ createMenuItem(s, "Go to definition...", ) -proc noRaise(x: proc()) {.raises: [].} = +proc noRaise(x: proc()) {.raises: [], effectsOf: x.} = # unknown call that might raise anything, but valid: x() @@ -34,3 +34,23 @@ proc use*() = use() + +# bug #12642 +import os + +proc raises() {.raises: Exception.} = discard +proc harmless() {.raises: [].} = discard + +let x = if paramStr(1) == "true": harmless else: raises + +let + choice = 0 + +proc withoutSideEffects(): int = 0 +proc withSideEffects(): int = echo "foo" # the echo causes the side effect + +let procPtr = case choice + of 0: withoutSideEffects + else: withSideEffects + +echo procPtr.repr diff --git a/tests/effects/teffects7.nim b/tests/effects/teffects7.nim index 73865b18d..9b7fbf5f0 100644 --- a/tests/effects/teffects7.nim +++ b/tests/effects/teffects7.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: ref ValueError" line: 10 """ - +{.push warningAsError[Effect]: on.} proc foo() {.raises: [].} = try: discard @@ -12,3 +12,5 @@ proc foo() {.raises: [].} = discard foo() + +{.pop.} diff --git a/tests/effects/teffects8.nim b/tests/effects/teffects8.nim index fb3c088d6..359b3a1df 100644 --- a/tests/effects/teffects8.nim +++ b/tests/effects/teffects8.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: Exception" line: 10 """ - +{.push warningAsError[Effect]: on.} proc foo() {.raises: [].} = try: discard @@ -10,3 +10,4 @@ proc foo() {.raises: [].} = raise foo() +{.pop.} diff --git a/tests/effects/teffectsmisc.nim b/tests/effects/teffectsmisc.nim new file mode 100644 index 000000000..8fb95b275 --- /dev/null +++ b/tests/effects/teffectsmisc.nim @@ -0,0 +1,39 @@ +discard """ + output: ''' +printing from adder +''' +""" + +import std/sugar + +block: + proc makeAdder(a: int): (int) -> void = + proc discard_adder(x: int) {.closure.} = + discard a + x + + proc echo_adder(x: int) {.closure.} = + echo("printing from adder") + + if a > 0: + discard_adder + else: + echo_adder + + let newAdder = makeAdder(0) + newAdder(5) + +block: + proc makeAdder(a: int): (int) -> void = + proc discard_adder(x: int) {.closure.} = + discard a + x + + proc echo_adder(x: int) {.closure.} = + echo("printing from adder") + + if a > 0: + echo_adder + else: + discard_adder + + let newAdder = makeAdder(0) + newAdder(5) diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim new file mode 100644 index 000000000..9934d27a7 --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate.nim @@ -0,0 +1,35 @@ +discard """ + errormsg: "cannot mutate location select(x, z).data within a strict func" + line: 35 +""" + +{.experimental: "strictFuncs".} + +type + Node = ref object + le, ri: Node + data: string + +func insert(x: var seq[Node]; yyy: Node) = + let L = x.len + x.setLen L + 1 + x[L] = yyy + +func len(n: Node): int = + var it = n + while it != nil: + inc result + it = it.ri + +func doNotDistract(n: Node) = + var m = Node(data: "abc") + +func select(a, b: Node): Node = b + +func mutate(n: Node) = + var it = n + let x = it + let y = x + let z = y + + select(x, z).data = "tricky" diff --git a/tests/effects/tfuncs_cannot_mutate2.nim b/tests/effects/tfuncs_cannot_mutate2.nim new file mode 100644 index 000000000..86f811017 --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate2.nim @@ -0,0 +1,24 @@ +discard """ + errormsg: "cannot mutate location x[0].a within a strict func" + line: 12 +""" + +{.experimental: "strictFuncs".} + +func copy[T](x: var openArray[T]; y: openArray[T]) = + for i in 0..high(x): + x[i] = y[i] + + x[0].a = nil + +type + R = ref object + a, b: R + data: string + +proc main = + var a, b: array[3, R] + b = [R(data: "a"), R(data: "b"), R(data: "c")] + copy a, b + +main() diff --git a/tests/effects/tfuncs_cannot_mutate3.nim b/tests/effects/tfuncs_cannot_mutate3.nim new file mode 100644 index 000000000..029152029 --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate3.nim @@ -0,0 +1,35 @@ +discard """ + errormsg: "cannot mutate location kid.parent within a strict func" + line: 16 +""" + +{.experimental: "strictFuncs".} + +type + Node = ref object + name: string + kids: seq[Node] + parent: Node + +func initParents(tree: Node) = + for kid in tree.kids: + kid.parent = tree + initParents(kid) + +proc process(intro: Node): Node = + var tree = Node(name: "root", kids: @[ + intro, + Node(name: "one", kids: @[ + Node(name: "two"), + Node(name: "three"), + ]), + Node(name: "four"), + ]) + initParents(tree) + +proc main() = + var intro = Node(name: "intro") + var tree = process(intro) + echo intro.parent.name + +main() diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim new file mode 100644 index 000000000..0ae4a0db9 --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate_simple.nim @@ -0,0 +1,19 @@ +discard """ + errormsg: '''cannot mutate location x.data within a strict func''' + line: 15 +""" + +{.experimental: "strictFuncs".} + +# bug #15508 + +type + MyType = ref object + data: string + +func edit(x: MyType) = + x.data = "hello" + +let x = MyType() +x.edit() +echo x.data diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim index 363624f19..cfac3ddd8 100644 --- a/tests/effects/tgcsafe.nim +++ b/tests/effects/tgcsafe.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'mainUnsafe' is not GC-safe" + errormsg: "'mainUnsafe' is not GC-safe as it performs an indirect call here" line: 26 cmd: "nim $target --hints:on --threads:on $options $file" """ @@ -13,7 +13,7 @@ proc myproc(i: int) {.gcsafe.} = if isNil(global_proc): return -proc mymap(x: proc ()) = +proc mymap(x: proc ()) {.effectsOf: x.} = x() var diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim index 07da4e3f8..6268592a9 100644 --- a/tests/effects/tgcsafe2.nim +++ b/tests/effects/tgcsafe2.nim @@ -1,5 +1,5 @@ discard """ - errormsg: '''type mismatch: got <proc (s: string){.locks: 0.}>''' + errormsg: '''type mismatch: got <proc (s: string)>''' line: 11 """ #5620 diff --git a/tests/effects/tgcsafe3.nim b/tests/effects/tgcsafe3.nim index 5137efe4c..36ea5112c 100644 --- a/tests/effects/tgcsafe3.nim +++ b/tests/effects/tgcsafe3.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'myproc' is not GC-safe as it accesses 'global_proc' which is a global using GC'ed memory" + errormsg: "'myproc' is not GC-safe as it calls 'global_proc'" line: 12 cmd: "nim $target --hints:on --threads:on $options $file" """ diff --git a/tests/effects/thooks.nim b/tests/effects/thooks.nim new file mode 100644 index 000000000..23cc005cd --- /dev/null +++ b/tests/effects/thooks.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--warningAsError:Effect" +""" + +import std/isolation + +# bug #23129 +type + Thing = object + x: string + +proc send(x: string) = + let wrapper = Thing(x: x) + discard isolate(wrapper) + +send("la") \ No newline at end of file diff --git a/tests/effects/tlaxeffects.nim b/tests/effects/tlaxeffects.nim new file mode 100644 index 000000000..7eedc372a --- /dev/null +++ b/tests/effects/tlaxeffects.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim $target $options --legacy:laxEffects $file" +""" + + +type + Foo = object + bar: seq[Foo] + +proc `==`(a, b: Foo): bool = + a.bar == b.bar diff --git a/tests/effects/tnestedprocs.nim b/tests/effects/tnestedprocs.nim new file mode 100644 index 000000000..125896d44 --- /dev/null +++ b/tests/effects/tnestedprocs.nim @@ -0,0 +1,63 @@ +discard """ + cmd: "nim check --hints:off $file" + nimout: '''tnestedprocs.nim(27, 8) Error: 'inner' can have side effects +> tnestedprocs.nim(29, 13) Hint: 'inner' calls `.sideEffect` 'outer2' +>> tnestedprocs.nim(26, 6) Hint: 'outer2' called by 'inner' + +tnestedprocs.nim(45, 8) Error: 'inner' can have side effects +> tnestedprocs.nim(47, 13) Hint: 'inner' calls `.sideEffect` 'outer6' +>> tnestedprocs.nim(44, 6) Hint: 'outer6' called by 'inner' + +tnestedprocs.nim(58, 41) Error: type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}' + Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'. +''' + errormsg: "type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}'" +""" +{.experimental: "strictEffects".} +proc outer {.noSideEffect.} = + proc inner(p: int) = + if p == 0: + outer() + + inner(4) + +outer() + +proc outer2 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + outer2() + + inner(4) + +outer2() + +proc outer3(p: int) {.noSideEffect.} = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + p.outer3() + + inner(4) + +outer3(5) + +proc outer6 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + outer6() + + inner(4) + echo "bad" + +outer6() + + +proc outer4 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + let x: proc () {.noSideEffect.} = outer4 + x() + + inner(4) + +outer4() diff --git a/tests/effects/tnosideeffect.nim b/tests/effects/tnosideeffect.nim new file mode 100644 index 000000000..9fc2f74d4 --- /dev/null +++ b/tests/effects/tnosideeffect.nim @@ -0,0 +1,24 @@ +block: # `.noSideEffect` + func foo(bar: proc(): int): int {.effectsOf: bar.} = bar() + var count = 0 + proc fn1(): int = 1 + proc fn2(): int = (count.inc; count) + + template accept(body) = + doAssert compiles(block: + body) + + template reject(body) = + doAssert not compiles(block: + body) + + accept: + func fun1() = discard foo(fn1) + reject: + func fun1() = discard foo(fn2) + + var foo2: type(foo) = foo + accept: + func main() = discard foo(fn1) + reject: + func main() = discard foo2(fn1) diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim deleted file mode 100644 index 00572aa6b..000000000 --- a/tests/effects/toutparam.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - cmd: '''nim c --warningAsError[Uninit]:on --skipCfg --skipParentCfg $file''' - errormsg: "use explicit initialization of 'x' for clarity [Uninit]" - line: 24 - disabled: "true" -""" - -proc gah[T](x: out T) = - x = 3 - -proc main = - var a: array[2, int] - var x: int - gah(x) - a[0] = 3 - a[x] = 3 - echo x - -main() - -proc mainB = - var a: array[2, int] - var x: int - a[0] = 3 - a[x] = 3 - echo x - -mainB() diff --git a/tests/effects/tstrict_caseobjects.nim b/tests/effects/tstrict_caseobjects.nim new file mode 100644 index 000000000..20bc810e0 --- /dev/null +++ b/tests/effects/tstrict_caseobjects.nim @@ -0,0 +1,47 @@ +discard """ + errormsg: "field access outside of valid case branch: x.x" + line: 45 +""" + +{.experimental: "strictCaseObjects".} + +type + NodeKind = enum + nkParent, + nkChild + + Node {.acyclic.} = ref object + case kind: NodeKind + of nkParent: + children: seq[Node] + of nkChild: + name: string + +let list = @[Node(kind: nkParent, children: @[]), Node(kind: nkChild, name: "hello")] +for node in list: + case node.kind + of nkChild: + echo $node.name # here this time there is a warning + else: discard + + +type + Foo = object + case b: bool + of false: + s: string + of true: + x: int + +var x = Foo(b: true, x: 4) +case x.b +of true: + echo x.x +of false: + echo "no" + +case x.b +of false: + echo x.x +of true: + echo "no" diff --git a/tests/effects/tstrict_effects.nim b/tests/effects/tstrict_effects.nim new file mode 100644 index 000000000..eee8fb71a --- /dev/null +++ b/tests/effects/tstrict_effects.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "s1 can raise an unlisted exception: CatchableError" + line: 27 +""" + +{.push warningAsError[Effect]: on.} +{.experimental: "strictEffects".} + +# bug #18376 + +{.push raises: [Defect].} +type Call = proc (x: int): int {.gcsafe, raises: [Defect, CatchableError].} + +type Bar* = object + foo*: Call + +proc passOn*(x: Call) = discard + +proc barCal(b: var Bar, s: string, s1: Call) = + #compiler complains that his line can throw CatchableError + passOn s1 + + +proc passOnB*(x: Call) {.effectsOf: x.} = discard + +proc barCal2(b: var Bar, s: string, s1: Call) = + passOnB s1 diff --git a/tests/effects/tstrict_effects2.nim b/tests/effects/tstrict_effects2.nim new file mode 100644 index 000000000..acc0a0540 --- /dev/null +++ b/tests/effects/tstrict_effects2.nim @@ -0,0 +1,28 @@ +discard """ + errormsg: "can raise an unlisted exception: Exception" + line: 23 +""" + +{.push warningAsError[Effect]: on.} +{.experimental: "strictEffects".} + +# bug #13905 + +proc atoi(v: cstring): cint {.importc: "atoi", cdecl, raises: [].} + +type Conv = proc(v: cstring): cint {.cdecl, raises: [].} + +var x: Conv = atoi + +# bug #17475 + +type + Callback = proc() + +proc f(callback: Callback) {.raises: [].} = + callback() + +proc main = + f(proc () = raise newException(IOError, "IO")) + +main() diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim new file mode 100644 index 000000000..0d98a0343 --- /dev/null +++ b/tests/effects/tstrict_effects3.nim @@ -0,0 +1,57 @@ +discard """ + action: compile +""" + +{.push warningAsError[Effect]: on.} + +{.experimental: "strictEffects".} + +proc fn(a: int, p1, p2: proc()) {.effectsOf: p1.} = + if a == 7: + p1() + if a<0: + raise newException(ValueError, $a) + +proc main() {.raises: [ValueError].} = + fn(1, proc()=discard, proc() = raise newException(IOError, "foo")) +main() + +# bug #19159 + +import macros + +func mkEnter() = + template helper = + discard + when defined pass: + helper() + else: + let ast = getAst(helper()) + + +# bug #6559 +type + SafeFn = proc (): void {. raises: [] } + +proc ok() {. raises: [] .} = discard +proc fail() {. raises: [] .} + +let f1 : SafeFn = ok +let f2 : SafeFn = fail + + +proc fail() = discard +f1() +f2() + +import std/json + +# bug #22254 +proc senri(a, b: seq[JsonNode]) {.raises: [].} = discard a == b + +# bug #22253 +proc serika() {.raises: [].} = discard default(JsonNode) == nil + +senri(@[newJBool(true)], @[newJBool(false)]) +serika() + diff --git a/tests/effects/tstrict_effects_sort.nim b/tests/effects/tstrict_effects_sort.nim new file mode 100644 index 000000000..8928ed0d3 --- /dev/null +++ b/tests/effects/tstrict_effects_sort.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "cmpE can raise an unlisted exception: Exception" + line: 27 +""" + +{.push warningAsError[Effect]: on.} + +{.experimental: "strictEffects".} + +import algorithm + +type + MyInt = distinct int + +var toSort = @[MyInt 1, MyInt 2, MyInt 3] + +proc cmpN(a, b: MyInt): int = + cmp(a.int, b.int) + +proc harmless {.raises: [].} = + toSort.sort cmpN + +proc cmpE(a, b: MyInt): int {.raises: [Exception].} = + cmp(a.int, b.int) + +proc harmfull {.raises: [].} = + toSort.sort cmpE diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim new file mode 100644 index 000000000..9d20f5d7e --- /dev/null +++ b/tests/effects/tstrict_funcs.nim @@ -0,0 +1,46 @@ +discard """ + cmd: "nim c --experimental:strictFuncs --experimental:views $file" +""" + +import tables, streams, parsecsv + +type + Contig2Reads = TableRef[string, seq[string]] + +proc get_Contig2Reads(sin: Stream, fn: string, contig2len: TableRef[string, int]): Contig2Reads = + result = newTable[string, seq[string]]() + var parser: CsvParser + open(parser, sin, filename = fn, separator = ' ', skipInitialSpace = true) + while readRow(parser, 2): + if contig2len.haskey(parser.row[1]): + mgetOrPut(result, parser.row[1], @[]).add(parser.row[0]) + + + +block: + # issue #15756 + func `&&&`[T](x: var seq[T], y: sink T): seq[T] = + newSeq(result, x.len + 1) + for i in 0..x.len-1: + result[i] = move(x[i]) + result[x.len] = move(y) + + var x = @[0, 1] + let z = x &&& 2 + + +func copy[T](x: var openArray[T]; y: openArray[T]) = + for i in 0..high(x): + x[i] = y[i] + +type + R = ref object + a, b: R + data: string + +proc main = + var a, b: array[3, R] + b = [R(data: "a"), R(data: "b"), R(data: "c")] + copy a, b + +main() diff --git a/tests/effects/tstrict_funcs_imports.nim b/tests/effects/tstrict_funcs_imports.nim new file mode 100644 index 000000000..bf68b61b2 --- /dev/null +++ b/tests/effects/tstrict_funcs_imports.nim @@ -0,0 +1,176 @@ +discard """ + cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views --threads:on -d:ssl -d:nimCoroutines $file" + targets: "c" +""" +{.warning[UnusedImport]: off.} + +when defined(linux): + import linenoise + +when defined(nimPreviewSlimSystem): + import std/[ + assertions, + formatfloat, + objectdollar, + syncio, + widestrs, + ] + +import + algorithm, + asyncdispatch, + asyncfile, + asyncfutures, + asynchttpserver, + asyncmacro, + asyncnet, + asyncstreams, + atomics, + base64, + bitops, + browsers, + cgi, + chains, + colors, + complex, + cookies, + coro, + cpuinfo, + cpuload, + critbits, + cstrutils, + deques, + distros, + dynlib, + encodings, + endians, + epoll, + fenv, + hashes, + heapqueue, + hotcodereloading, + htmlgen, + htmlparser, + httpclient, + httpcore, + inotify, + intsets, + json, + kqueue, + lenientops, + lexbase, + lists, + locks, + logging, + macrocache, + macros, + marshal, + math, + memfiles, + mersenne, + mimetypes, + nativesockets, + net, + nimhcr, + # nimprof, + nre, + oids, + options, + os, + osproc, + parsecfg, + parsecsv, + parsejson, + parseopt, + parsesql, + parseutils, + parsexml, + pathnorm, + pegs, + posix_utils, + prelude, + random, + rationals, + rdstdin, + re, + registry, + reservedmem, + rlocks, + ropes, + rtarrays, + selectors, + sequtils, + sets, + sharedlist, + sharedtables, + ssl_certs, + ssl_config, + stats, + streams, + streamwrapper, + strformat, + strmisc, + strscans, + strtabs, + strutils, + sugar, + tables, + terminal, + threadpool, + times, + typeinfo, + typetraits, + unicode, + unidecode, + unittest, + uri, + volatile, + winlean, + xmlparser, + xmltree + +import experimental/[ + diff, +] + +import packages/docutils/[ + highlite, + rst, + rstast, + rstgen, +] + +import std/[ + compilesettings, + decls, + editdistance, + effecttraits, + enumerate, + enumutils, + exitprocs, + isolation, + jsonutils, + logic, + monotimes, + packedsets, + setutils, + socketstreams, + stackframes, + sums, + time_t, + varints, + with, + wordwrap, + wrapnils, +] + +import std/private/[ + asciitables, + decode_helpers, + gitutils, + globs, + miscdollars, + since, + strimpl, + underscored_calls, +] diff --git a/tests/effects/tstrict_funcs_imports_js.nim b/tests/effects/tstrict_funcs_imports_js.nim new file mode 100644 index 000000000..667887ff0 --- /dev/null +++ b/tests/effects/tstrict_funcs_imports_js.nim @@ -0,0 +1,20 @@ +discard """ + cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views $file" + targets: "js" +""" +{.warning[UnusedImport]: off.} + +import + asyncjs, + dom, + jsconsole, + jsffi, + jsre + +import std/[ + jsbigints, +] + +import std/private/[ + jsutils, +] diff --git a/tests/effects/tstrictfuncs_misc.nim b/tests/effects/tstrictfuncs_misc.nim new file mode 100644 index 000000000..8c573bb3a --- /dev/null +++ b/tests/effects/tstrictfuncs_misc.nim @@ -0,0 +1,65 @@ +discard """ + action: compile +""" + +{.experimental: "strictFuncs".} + +func sortedFake1[T](a: openArray[T]): seq[T] = + for i in 0 .. a.high: result.add a[i] +func sortedFake2[T](a: openArray[T]): seq[T] = + result = newSeq[T](a.len) + for i in 0 .. a.high: result[i] = a[i] +type Foo1 = object +type Foo2 = ref object +block: + let a1 = sortedFake1([Foo1()]) # ok + let a2 = sortedFake1([Foo2()]) # ok +block: + let a1 = sortedFake2([Foo1()]) # ok + let a2 = sortedFake2([Foo2()]) # error: Error: 'sortedFake2' can have side effects + + +import std/sequtils +type Foob = ref object + x: int +let a1 = zip(@[1,2], @[1,2]) # ok +let a2 = zip(@[Foob(x: 1)], @[Foob(x: 2)]) # error in 1.6.0 RC2, but not 1.4.x + + +# bug #20863 +type + Fooc = ref object + +func twice(foo: Fooc) = + var a = newSeq[Fooc](2) + a[0] = foo # No error. + a[1] = foo # Error: 'twice' can have side effects. + +let foo = Fooc() +twice(foo) + +# bug #17387 +import json + +func parseColumn(columnNode: JsonNode) = + let columnName = columnNode["name"].str + +parseColumn(%*{"a": "b"}) + +type + MyTable = object + data: seq[int] + + JsonNode3 = ref object + fields: MyTable + +proc `[]`(t: MyTable, key: string): int = + result = t.data[0] + +proc `[]`(x: JsonNode3, key: string): int = + result = x.fields[key] + +func parseColumn(columnNode: JsonNode3) = + var columnName = columnNode["test"] + +parseColumn(JsonNode3()) |