-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
(Edited) better error messages when Future[T] is not used #11912
Comments
This is currently possible with term rewriting macros. import async
template discardedFuture{discard fut}(fut: Future) {.
error: "cannot discard future, use asyncCheck instead".} = discard
proc foo {.async.} = discard
discard foo() |
holy wow, that's amazing if it works. @hlaaftana you wanna make a PR? |
On it |
It's a race to the bottom otherwise, async is complex and you need to read its documentation. We cannot produce a system where you never have to read anything and even if we did, people then complain about other things. |
Huh? Why reject this if it works? Your comment gives no reasoning as far as I can see. |
I see what happened (#14176). Once again I would have appreciated a ping on a clearly async-related PR. Now can we get the code that @hlaaftana suggested above? Is the only reason you rejected it because a test (which is incorrect as far as I can see) used |
template discardedFuture{discard call(a)}(call: proc, a: varargs[untyped]) =
when call(a) is Future:
static:
error "cannot discard an async call, use asyncCheck instead"
else:
discard call(a) hm! EDIT: add else! |
after some discussions with @disruptek I agreed that async calls should be separated from futures: I want to see the first one not discardable , so it seems this variation of the original author's template maybe does it? i tested it a bit with import asyncdispatch, macros
template discardedFuture{discard call(a)}(call: proc, a: varargs[untyped]) =
when call(a) is Future:
static:
error "cannot discard an async call, use asyncCheck instead"
else:
discard call(a)
proc sync =
raise newException(ValueError, "e")
proc aSync0(b: int): int =
echo b
proc a2(b: int, b2: int) {.async.} =
echo 0
proc a0 {.async.} =
echo 0
proc a(b: int) {.async.} =
if b == 0:
sync()
else:
echo 0
proc run {.async.} =
var fut = newFuture[int]()
discard fut
asyncCheck a0()
discard a2(0, 0)
discard a(0)
discard aSync0(0)
waitFor run() thanks again to the original author! |
ok, it seems some we get into some kind of recursion which is limited tho with discard in else : i am not sure how to match just proc returning Future, sorry, but we can generate discard in a different way like let ignore ? |
Still 👎 because it's still a solution to a problem that doesn't exist. |
template discardedFuture{discard call(a)}(call: proc, a: varargs[untyped]) =
when call(a) is Future:
static:
error "cannot discard an async call, use asyncCheck instead"
else:
discard call(a) This recurses the template a good 50 times (in the else branch). If I remember there is some pragma that lets you not recurse but I can't find what it is. If async calls are bad, and the option that isn't async "calls" is |
There is another, simpler solution to all of this. import async
template discardedFutureError{discard fut}(fut: Future) {.
error: "cannot discard future, use asyncCheck instead".} = discard
type ProperlyDiscardedFuture*[T] = distinct Future[T]
template properlyDiscard[T](fut: Future[T]) =
discard ProperlyDiscardedFuture[T](fut)
proc foo() {.async.} = discard
discard foo() # error
properlyDiscard foo() # fine This, coupled with every other alternative presented here and in #14176, should solve all your problems. This language is too powerful lol |
ah smart! but no , i agree with disruptek, discard for futures should stay, but i still think discard for async calls only should be detected the rewriting thing works easily: there should be a way to match that, and even if not, one can define a concept for proc returning a future (with a macro for returnType) and probably easier ways are possible |
And i can't fully agree with @disruptek, there is nothing inherently correct about hiding errors with it's just seems as a bad surprise for users waiting to happen and a recipe for bugs imho, sorry if i am wrong: silent runtime errors are not something we want, they seem worse than compile errors and than normal runtime errors: so let's use try except or "ignoreErrors" for such Again, i am talking only about async calls not futures |
They would stay. With distinct types or The only inconsistency here, is what @Araq said, term rewriting is a bad solution. They're not meant for this purpose. The problem is we should let users know at the code level that discarding futures is wrong, warning or error (documentation is cool but the code should be self-sufficient for information), and we're doing it by rewriting AST. |
I don't agree with that:
|
I've given up, sorry. I don't think there's a solution here that will appease everyone. Your call solution is more like a |
Well it's a valid discussion from all sides Forgive me, @disruptek for my yesterday's irc/gitter behavior/attacks: I was indeed mistaken in my try/except argument, as i didnt think about how async does insert an invisible try/except by design, as well as other hypothetical macros: so this wasnt a good argument and i was way too aggressive about all that. I still believe some kind of detection of |
So then we have a system where |
No, I rejected it because it used a TR macro for detection and TR macros are reserved for custom optimizations by design. Misusing language features in order to prevent misusing other language features isn't good. And that doesn't have to do much with async at all, so you weren't involved. |
Okay, after discussing with Araq on IRC I agree that we shouldn't pick and choose for which types One important thing is that the compiler leads new users in a wrong path:
Gives: Immediate fix for this is to s/discarded/used/, but in the long term the best thing would be to suggest Regarding some of the other responses here:
How did you come to that conclusion @alehander92? Any future can be |
Must have thought about something like this: let fut = foo()
discard fut
discard fut
asyncCheck fut Which also points out how general of a feature That error message should be changed, but not specifically for I think we should also rename
Void futures aren't the only thing that have to be discarded and |
Summary
The docs say that we shouldn't discard futures, but Nim should really catch that on compile time if possible.
Description
The easiest way should be by adding an {.undiscardable.} pragma for types, using it for Future and making nim generate a compile time error discarding such types on sem checking: I can open a PR, if this is accepted
Alternatives
Just forbidding discarding Future, but not sure how this will work without somehow hardcoding that (the user might have different Future type)
The text was updated successfully, but these errors were encountered: