-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
raises tracking should behave like nosideeffect tracking: effects through params should be allowed #403
Comments
this solves a different problem than |
I'm confused, nim-lang/Nim#18376 clearly shows that the rules as they already exist are not ok and we need a language extension. This is from our test suite (teffects6.nim) (and from the spec too) proc noRaise(x: proc()) {.raises: [].} =
# unknown call that might raise anything, but valid:
x()
proc doRaise() {.raises: [IoError].} =
raise newException(IoError, "IO")
proc use*() =
noRaise(doRaise)
# Here the compiler inferes that EIO can be raised. it already works as you propose it should work. And it's insuffient, see bug #18376 |
@Araq no it doesn't work as I proposed, see the detailed step-by-step reduction in nim-lang/Nim#18376 (comment) and the final reduction: when defined case7g:
type Call1 = proc() # can raise anything, including ValueError
type Call2 = proc() {.raises: [ValueError].} # can raise at most ValueError
proc barCal(b1: Call1, b2: Call2) {.raises: [].} =
b1() # ok
b2() # BUG: compiler rejects with: Error: b2() can raise an unlisted exception: ValueError |
Here's a variant of this RFC, which tracks effects through params via symbol references: proc fn1(a: proc(), b: proc()) # inferred as {.raises: Any.}
proc fn2(a: proc(), b: proc()) {.raises: [].} # raises nothing
proc fn3(a: proc(), b: proc()) {.raises: [a].} # raises whatever param `a` raises, ie raises(a)
proc fn4(a: proc(), b: proc()) {.raises: [a, b, ValueError].} # raises raises(a) + raises(b) + ValueError
proc fn5(a: proc(), b: proc()) = # inferred as {.raises(a).}
a() benefits
proc fn1(a: proc()): auto = a # inferred as {.raises: [].}
proc fn2(a: proc()): auto = a() # inferred as {.raises: [a].}
proc fn3(a: proc()): auto = # inferred as {.raises: [a, ValueError].}
if a(): raise newException(ValueError, "foo")
proc sorted*(a: seq[T], cmp: proc(a, b: T): int): seq[T] = ...
# infered as: {.raises: [cmp].} since cmp is called
proc main1(): auto = # infered as {.raises: [].}
proc cmp(a,b): int = b-a # infered as {.raises: [].}
sorted([1,2])
proc main2(): auto = # infered as {.raises: [ValueError].} because of raises: [cmp]
proc cmp2(a, b: string): int = cmp(a.parseInt, b.parseInt) # can raise ValueError
sorted(@["12", "3", "01"], cmp2)
notenim-lang/Nim#14976 implements a similar thing, but in the case of raises this is actually a simpler analysis, more local (doesn't require interprocedural analysis, all info is summarized in the raises annotation). |
You are right, but it's only one aspect of the problem: proc x(...; callback: proc()) {.raises: [].} =
# if `add` raises nothing, so does this:
container.add callback
Your later reply seems to acknowledge that and is a better proposal. |
Very good RFC. |
lgtm - one more issue in nim in general is that the raises list of
Call operators solve this in C++ - I believe there have been similar requests for nim so that One should also be careful to ensure that the binding of |
no, this would depend on call site; proc main() = # infered as raises: []
proc a()=discard # infered as raises: []
proc b()=discard
fn3(a, b) # infered as raises: [] because fn3 is `raises: [a]` where a is raises: []`
proc main2() = # infered as raises: [ValueError]
proc a()=raise newException(ValueError, "") # # infered as raises: [ValueError]
proc b()=discard
fn3(a, b) # infered as raises: [ValueError]
nim-lang/Nim#11992 would enable that among many other things but I dont' see how call operators or inlining relates to
unlikely to clash bc params are typically lowercase and exceptions types are typically uppercase, but even then usual scoping rules apply and can be disambiguated with fully qualified names # from customexceptions import Foo1
proc a(Foo1: proc(), Foo2: proc()) {.raises: [Foo2, customexceptions.Foo1] |
well, it doesn't, currently. when you type
if |
it does, see below
this is false: proc fn1() = discard
proc fn2() = raise newException(ValueError, "")
proc bar(fn: proc()) = discard # depends on call site
proc main() {.raises: [].} =
bar(fn1) # ok
# bar(fn2) # would give CT error Your example doesn't negate that; |
Please make #403 (comment) it's own RFC or patch this RFC to make this the top comment. |
done, see #408 |
in this example, the fact that the pessimistic analysis is necessary, or the compiler must generate a new overload for every variation of the raises effects because the behaviour of |
I intended to write proc fn1() = discard
proc fn2() = raise newException(ValueError, "")
proc bar(fn: proc()) = fn() # depends on call site
proc main() {.raises: [].} =
bar(fn1) # ok
# bar(fn2) # would give CT error |
here's my thought regarding
effectsDelayed
(nim-lang/Nim#18610): IMO what we should do instead is same as what we do for{.nosideeffect.}
, where a func is allowed to have side effects so long they're only through parameters:nosideeffect
: effects are allowed through params:raises
: effects should also be allowed through params:so far so good, it behaves the same as
nosideeffect
.The problem is the following; which is what this RFC is about:
proposal
effectsDelayed
, it's not needednodesideeffect
, such that effects are allowed through paramsnote
this is analog to how purity works in D, eg: https://forum.dlang.org/thread/[email protected]
and related articles, eg: https://klickverbot.at/blog/2012/05/purity-in-d/
The text was updated successfully, but these errors were encountered: