From experience using nurseries for over 3 years in a large, production, Python app: cancelling children on non-error scope exit is not the desired semantics, 95% of the time.
I think it depends on what the "tasks" are for. If a task is a kind of job that runs in a finite amount of time and produces some useful result/computation, then why would you issue a return statement before they have completed? Don't you have to wait for them to complete in order to get their results, and then use those to produce the return value or as input to a subsequent computation? And if that is not the case, then I would argue that those tasks are not essential, and could just as well be cancelled upon issuing the return statement (which causes control flow to leave the current scope).
If we insist on waiting for the tasks to complete whenever we leave the scope for non-error reasons, then we also have an asymmetry/inconsistency of behavior when leaving the scope; i.e., the fate of the tasks depends on the precise way in which we leave the scope, which I think makes programs harder to reason about in a way.
In my own use cases of coroutines (an event-driven game which happens to be in C++, but the ideas are the same[1]) I've found the right default to be that all tasks get cancelled when leaving a scope by any means. Any tasks that we want to complete must be explicitly awaited on (either on their own, or with e.g. wait_for_any, wait_for_all, etc.).
David
[1] Currently C++ coroutines are not able to yield at scope exit, unlike Lua. And so therefore, cancellation-on-scope-exit is the only option (and even that cannot be implemented in an ideal way because sometimes even cancellation requires yielding). But I actually think it can be the right default in some applications. But I do understand that it might not be the right default in all cases; some C++ developers are currently expressing dismay at the fact that coroutines cannot yield within destructors.