From 9fb0b32acf557ec91c24ad54e59c67ee50726117 Mon Sep 17 00:00:00 2001 From: Lancer11211 <109360848+Lancer11211@users.noreply.github.com> Date: Tue, 26 Jul 2022 07:40:49 +0200 Subject: [PATCH] .forbids pragma: defining forbidden tags (#20050) * .forbids pragma: defining illegal effects for proc types This patch intends to define the opposite of the .tags pragma: a way to define effects which are not allowed in a proc. * updated documentation and changelogs for the forbids pragma * renamed notTagEffects to forbiddenEffects * corrected issues of forbids pragma the forbids pragma didn't handle simple restrictions properly and it also had issues with subtyping * removed incorrect character from changelog * added test to cover the interaction of methods and the forbids pragma * covering the interaction of the tags and forbids pragmas * updated manual about the forbids pragma * removed useless statement * corrected the subtyping of proc types using the forbids pragma * updated manual for the forbids pragma * updated documentations for forbids pragma * updated nim docs * updated docs with rsttester.nim * regenerated documentation * updated rst docs * Update changelog.md Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> * updated changelog * corrected typo Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- changelog.md | 2 + compiler/ast.nim | 3 +- compiler/docgen.nim | 4 +- compiler/pragmas.nim | 12 ++-- compiler/semcall.nim | 4 +- compiler/sempass2.nim | 56 +++++++++++++-- compiler/semstmts.nim | 2 +- compiler/types.nim | 18 +++++ compiler/vmops.nim | 2 + compiler/wordrecg.nim | 4 +- doc/manual.md | 47 +++++++++++- lib/std/effecttraits.nim | 9 +++ .../expected/index.html | 2 +- .../expected/subdir/subdir_b/utils.html | 36 +++++----- nimdoc/testproject/expected/testproject.html | 71 ++++++++++--------- tests/effects/teffects11.nim | 21 ++++++ tests/effects/teffects12.nim | 52 ++++++++++++++ tests/effects/teffects13.nim | 19 +++++ tests/effects/teffects14.nim | 15 ++++ tests/effects/teffects15.nim | 18 +++++ tests/effects/teffects16.nim | 20 ++++++ tests/effects/teffects17.nim | 17 +++++ tests/effects/teffects18.nim | 19 +++++ tests/effects/teffects19.nim | 23 ++++++ 24 files changed, 405 insertions(+), 71 deletions(-) create mode 100644 tests/effects/teffects11.nim create mode 100644 tests/effects/teffects12.nim create mode 100644 tests/effects/teffects13.nim create mode 100644 tests/effects/teffects14.nim create mode 100644 tests/effects/teffects15.nim create mode 100644 tests/effects/teffects16.nim create mode 100644 tests/effects/teffects17.nim create mode 100644 tests/effects/teffects18.nim create mode 100644 tests/effects/teffects19.nim diff --git a/changelog.md b/changelog.md index d8f3ccb928b17..c698f09d7619b 100644 --- a/changelog.md +++ b/changelog.md @@ -67,6 +67,8 @@ becomes an alias for `addr`. ## Language changes +- [Tag tracking](manual.html#tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma + which can be used to disable certain effects in proc types. - [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. - Templates now accept [macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas). diff --git a/compiler/ast.nim b/compiler/ast.nim index 6610a1333d327..27e4fab63374b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -347,7 +347,8 @@ const ensuresEffects* = 2 # 'ensures' annotation tagEffects* = 3 # user defined tag ('gc', 'time' etc.) pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type - effectListLen* = 5 # list of effects list + forbiddenEffects* = 5 # list of illegal effects + effectListLen* = 6 # list of effects list nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} # these must be last statements in a block diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d728c535fb0db..0ca1b8c52a4e9 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1212,8 +1212,9 @@ proc documentRaises*(cache: IdentCache; n: PNode) = let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes") let p4 = documentNewEffect(cache, n) let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes") + let p6 = documentEffect(cache, n, pragmas, wForbids, forbiddenEffects) - if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: + if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil or p6 != nil: if pragmas.kind == nkEmpty: n[pragmasPos] = newNodeI(nkPragma, n.info) if p1 != nil: n[pragmasPos].add p1 @@ -1221,6 +1222,7 @@ proc documentRaises*(cache: IdentCache; n: PNode) = if p3 != nil: n[pragmasPos].add p3 if p4 != nil: n[pragmasPos].add p4 if p5 != nil: n[pragmasPos].add p5 + if p6 != nil: n[pragmasPos].add p6 proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = ## Goes through nim nodes recursively and collects doc comments. diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1487a871d2f4f..417941cd1b59e 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -32,7 +32,7 @@ const wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wDelegator, wGcSafe, + wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, wRequires, wEnsures, wEnforceNoRaises} converterPragmas* = procPragmas @@ -45,7 +45,7 @@ const iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wMagic, wBorrow, wDiscardable, wGensym, wInject, wRaises, wEffectsOf, - wTags, wLocks, wGcSafe, wRequires, wEnsures} + wTags, wForbids, wLocks, wGcSafe, wRequires, wEnsures} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} stmtPragmas* = { wHint, wWarning, wError, @@ -65,7 +65,7 @@ const lambdaPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, - wRaises, wLocks, wTags, wRequires, wEnsures, wEffectsOf, + wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf, wGcSafe, wCodegenDecl, wNoInit, wCompileTime} typePragmas* = declPragmas + {wMagic, wAcyclic, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, @@ -85,7 +85,7 @@ const paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, - wThread, wRaises, wEffectsOf, wLocks, wTags, wGcSafe, + wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, wRequires, wEnsures} forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas @@ -820,7 +820,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, elif not isStatement: localError(c.config, n.info, "'cast' pragma only allowed in a statement context") case whichPragma(key[1]) - of wRaises, wTags: pragmaRaisesOrTags(c, key[1]) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, key[1]) else: discard return elif key.kind notin nkIdentKinds: @@ -1184,7 +1184,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym == nil: invalidPragma(c, it) of wLine: pragmaLine(c, it) - of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(c, it) elif sym.typ == nil: invalidPragma(c, it) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 36658d472d610..4f956785e0fdd 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -154,6 +154,8 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) = "proc with {.locks: 0.} to get extended error information." of efEffectsDelayed: result.add "\n The `.effectsOf` annotations differ." + of efTagsIllegal: + result.add "\n The `.forbids` requirements caught an illegal tag." when defined(drnim): if not c.graph.compatibleProps(c.graph, f, a): result.add "\n The `.requires` or `.ensures` properties are incompatible." @@ -730,4 +732,4 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = result = nil elif result.magic in {mArrPut, mArrGet}: # cannot borrow these magics for now - result = nil \ No newline at end of file + result = nil diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0ee806f3678fc..1e3e4135080ba 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -66,6 +66,7 @@ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags + forbids: PNode # list of tags bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn: int owner: PSym ownerModule: PSym @@ -388,6 +389,12 @@ proc addTag(a: PEffects, e, comesFrom: PNode) = if sameType(aa[i].typ.skipTypes(skipPtrs), e.typ.skipTypes(skipPtrs)): return throws(a.tags, e, comesFrom) +proc addNotTag(a: PEffects, e, comesFrom: PNode) = + var aa = a.forbids + for i in 0..Procs
-
proc foo() {....raises: [], tags: [].}
+
proc foo() {....raises: [], tags: [], forbids: [].}
I do foo diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 4d753ad8f8925..c42f185905f84 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -278,7 +278,7 @@

Procs

-
func `'big`(a: string): SomeType {....raises: [], tags: [].}
+
func `'big`(a: string): SomeType {....raises: [], tags: [], forbids: [].}
@@ -323,7 +323,7 @@

Procs

-
proc f(x: G[int]) {....raises: [], tags: [].}
+
proc f(x: G[int]) {....raises: [], tags: [], forbids: [].}
There is also variant f(G[string]) @@ -331,7 +331,7 @@

Procs

-
proc f(x: G[string]) {....raises: [], tags: [].}
+
proc f(x: G[string]) {....raises: [], tags: [], forbids: [].}
See also f(G[int]). @@ -353,7 +353,7 @@

Procs

-
proc fn2() {....raises: [], tags: [].}
+
proc fn2() {....raises: [], tags: [], forbids: [].}
comment @@ -361,7 +361,7 @@

Procs

-
proc fn2(x: int) {....raises: [], tags: [].}
+
proc fn2(x: int) {....raises: [], tags: [], forbids: [].}
fn2 comment @@ -369,7 +369,7 @@

Procs

-
proc fn2(x: int; y: float) {....raises: [], tags: [].}
+
proc fn2(x: int; y: float) {....raises: [], tags: [], forbids: [].}
@@ -380,7 +380,7 @@

Procs

-
proc fn3(): auto {....raises: [], tags: [].}
+
proc fn3(): auto {....raises: [], tags: [], forbids: [].}
comment @@ -391,7 +391,7 @@

Procs

-
proc fn4(): auto {....raises: [], tags: [].}
+
proc fn4(): auto {....raises: [], tags: [], forbids: [].}
comment @@ -402,7 +402,7 @@

Procs

-
proc fn5() {....raises: [], tags: [].}
+
proc fn5() {....raises: [], tags: [], forbids: [].}
comment @@ -413,7 +413,7 @@

Procs

-
proc fn6() {....raises: [], tags: [].}
+
proc fn6() {....raises: [], tags: [], forbids: [].}
comment @@ -424,7 +424,7 @@

Procs

-
proc fn7() {....raises: [], tags: [].}
+
proc fn7() {....raises: [], tags: [], forbids: [].}
comment @@ -435,7 +435,7 @@

Procs

-
proc fn8(): auto {....raises: [], tags: [].}
+
proc fn8(): auto {....raises: [], tags: [], forbids: [].}
comment @@ -446,7 +446,7 @@

Procs

-
func fn9(a: int): int {....raises: [], tags: [].}
+
func fn9(a: int): int {....raises: [], tags: [], forbids: [].}
comment @@ -457,7 +457,7 @@

Procs

-
func fn10(a: int): int {....raises: [], tags: [].}
+
func fn10(a: int): int {....raises: [], tags: [], forbids: [].}
comment @@ -468,7 +468,7 @@

Procs

-
func fN11() {....raises: [], tags: [].}
+
func fN11() {....raises: [], tags: [], forbids: [].}
@@ -476,7 +476,7 @@

Procs

-
func fN11(x: int) {....raises: [], tags: [].}
+
func fN11(x: int) {....raises: [], tags: [], forbids: [].}
@@ -498,7 +498,7 @@

Procs

-
proc someType(): SomeType {....raises: [], tags: [].}
+
proc someType(): SomeType {....raises: [], tags: [], forbids: [].}
constructor. @@ -515,7 +515,7 @@

Iterators

-
iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [].}
+
iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [], forbids: [].}
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 5fb9e697cb5e7..e18625222e133 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -471,7 +471,7 @@

Procs

-
proc addfBug14485() {....raises: [], tags: [].}
+
proc addfBug14485() {....raises: [], tags: [], forbids: [].}
Some proc @@ -494,7 +494,7 @@

Procs

-
proc anything() {....raises: [], tags: [].}
+
proc anything() {....raises: [], tags: [], forbids: [].}
There is no block quote after blank lines at the beginning. @@ -506,7 +506,7 @@

Procs

proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
-                                tags: [RootEffect].}
+ tags: [RootEffect], forbids: [].}
ok1 @@ -517,7 +517,8 @@

Procs

-
proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
+
proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
+                                        forbids: [].}
@@ -528,7 +529,8 @@

Procs

-
proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
+
proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
+                                        forbids: [].}
@@ -552,7 +554,7 @@

Procs

-
proc baz() {....raises: [], tags: [].}
+
proc baz() {....raises: [], tags: [], forbids: [].}
@@ -589,7 +591,7 @@

Procs

proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
-    header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [].}
+ header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [], forbids: [].}
@@ -601,7 +603,8 @@

Procs

proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
-                                     varargs, discardable, ...raises: [], tags: [].}
+ varargs, discardable, ...raises: [], tags: [], + forbids: [].}
the c printf. etc. @@ -612,7 +615,7 @@

Procs

-
proc fromUtils3() {....raises: [], tags: [].}
+
proc fromUtils3() {....raises: [], tags: [], forbids: [].}
came form utils but should be shown where fromUtilsGen is called @@ -638,7 +641,7 @@

Procs

proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
-    ...raises: [], tags: [].}
+ ...raises: [], tags: [], forbids: [].}

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

@@ -654,7 +657,7 @@

Procs

proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
-    ...raises: [], tags: [].}
+ ...raises: [], tags: [], forbids: [].}

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

@@ -671,7 +674,7 @@

Procs

-
proc p1() {....raises: [], tags: [].}
+
proc p1() {....raises: [], tags: [], forbids: [].}
cp1 @@ -698,7 +701,7 @@

Procs

-
func someFunc() {....raises: [], tags: [].}
+
func someFunc() {....raises: [], tags: [], forbids: [].}
My someFunc. Stuff in quotes here. Some link @@ -709,7 +712,7 @@

Procs

-
proc tripleStrLitTest() {....raises: [], tags: [].}
+
proc tripleStrLitTest() {....raises: [], tags: [], forbids: [].}
@@ -756,7 +759,7 @@

Procs

-
proc z1(): Foo {....raises: [], tags: [].}
+
proc z1(): Foo {....raises: [], tags: [], forbids: [].}
cz1 @@ -767,7 +770,7 @@

Procs

-
proc z2() {....raises: [], tags: [].}
+
proc z2() {....raises: [], tags: [], forbids: [].}
cz2 @@ -780,7 +783,7 @@

Procs

-
proc z3() {....raises: [], tags: [].}
+
proc z3() {....raises: [], tags: [], forbids: [].}
cz3 @@ -791,7 +794,7 @@

Procs

-
proc z4() {....raises: [], tags: [].}
+
proc z4() {....raises: [], tags: [], forbids: [].}
cz4 @@ -802,7 +805,7 @@

Procs

-
proc z5(): int {....raises: [], tags: [].}
+
proc z5(): int {....raises: [], tags: [], forbids: [].}
cz5 @@ -813,7 +816,7 @@

Procs

-
proc z6(): int {....raises: [], tags: [].}
+
proc z6(): int {....raises: [], tags: [], forbids: [].}
cz6 @@ -824,7 +827,7 @@

Procs

-
proc z7(): int {....raises: [], tags: [].}
+
proc z7(): int {....raises: [], tags: [], forbids: [].}
cz7 @@ -835,7 +838,7 @@

Procs

-
proc z8(): int {....raises: [], tags: [].}
+
proc z8(): int {....raises: [], tags: [], forbids: [].}
cz8 @@ -846,7 +849,7 @@

Procs

-
proc z9() {....raises: [], tags: [].}
+
proc z9() {....raises: [], tags: [], forbids: [].}
@@ -859,7 +862,7 @@

Procs

-
proc z10() {....raises: [], tags: [].}
+
proc z10() {....raises: [], tags: [], forbids: [].}
@@ -872,7 +875,7 @@

Procs

-
proc z11() {....raises: [], tags: [].}
+
proc z11() {....raises: [], tags: [], forbids: [].}
@@ -885,7 +888,7 @@

Procs

-
proc z12(): int {....raises: [], tags: [].}
+
proc z12(): int {....raises: [], tags: [], forbids: [].}
@@ -898,7 +901,7 @@

Procs

-
proc z13() {....raises: [], tags: [].}
+
proc z13() {....raises: [], tags: [], forbids: [].}
cz13 @@ -911,7 +914,7 @@

Procs

-
proc z17() {....raises: [], tags: [].}
+
proc z17() {....raises: [], tags: [], forbids: [].}
cz17 rest @@ -930,7 +933,7 @@

Methods

-
method method1(self: Moo) {.base, ...raises: [], tags: [].}
+
method method1(self: Moo) {.base, ...raises: [], tags: [], forbids: [].}
foo1 @@ -941,7 +944,7 @@

Methods

-
method method2(self: Moo): int {.base, ...raises: [], tags: [].}
+
method method2(self: Moo): int {.base, ...raises: [], tags: [], forbids: [].}
foo2 @@ -952,7 +955,7 @@

Methods

-
method method3(self: Moo): int {.base, ...raises: [], tags: [].}
+
method method3(self: Moo): int {.base, ...raises: [], tags: [], forbids: [].}
foo3 @@ -969,7 +972,7 @@

Iterators

-
iterator fromUtils1(): int {....raises: [], tags: [].}
+
iterator fromUtils1(): int {....raises: [], tags: [], forbids: [].}
@@ -984,7 +987,7 @@

Iterators

-
iterator iter1(n: int): int {....raises: [], tags: [].}
+
iterator iter1(n: int): int {....raises: [], tags: [], forbids: [].}
foo1 @@ -995,7 +998,7 @@

Iterators

-
iterator iter2(n: int): int {....raises: [], tags: [].}
+
iterator iter2(n: int): int {....raises: [], tags: [], forbids: [].}
foo2 diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim new file mode 100644 index 0000000000000..e4098e9594f30 --- /dev/null +++ b/tests/effects/teffects11.nim @@ -0,0 +1,21 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +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 0000000000000..5f5499c3853b1 --- /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 0000000000000..73082f99784e6 --- /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 0000000000000..6291d95696dd1 --- /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 0000000000000..c3079cdbc1d75 --- /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 0000000000000..ee8f782a3a89a --- /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 0000000000000..5e6b838962d15 --- /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 0000000000000..576e766358200 --- /dev/null +++ b/tests/effects/teffects18.nim @@ -0,0 +1,19 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +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 0000000000000..49fb87523b39d --- /dev/null +++ b/tests/effects/teffects19.nim @@ -0,0 +1,23 @@ +discard """ +action: compile +errormsg: "type mismatch: got " +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)