Your examples with String seem irrelevant, since it's both Copyable and Sendable, which means it doesn't interact with sending at all. If you want to fairly compare reference and value types, you need to construct a non-Sendable value type, and have a clear way to control whether they're considered to be "in their own region" or not.
consuming is for the caller only — if the type is non-Copyable, whatever's passed into the argument is unavailable afterward in the caller (it affects refcounting behavior in both caller and callee for reference types, but mostly that doesn't matter).
sending is for both the caller and callee — in the caller you must prove the value is "in its own region", and in the callee you may assume the value is "in its own region" (which allows it to be safely transferred to another isolation).
There is only a slight relationship between those things — if a value is non-Copyable then it can't be used again in the caller whether it's sent to a sending parameter or a consuming parameter (not that a borrowing sending parameter makes any sense anyway).
Once-callability and sending don't intersect any more strongly either — just because a once-callable closure would be non-Copyable and have a consuming call operator, it could still be "in its own region" (and therefore eligible for sending) or not, depending on what it captures.
I'm afraid I can't agree. I was aware of the difference you mentioned. I believe you meant the code like the following, in which str in test() and str in send(_:) are two different copies.
However, test2() in #87076 is different. The closure of capture() captures a str in test function by reference. A closure capturing a mutable value is non-Sendable. It's sending only when the mutable value it captures is transferred together with it. That's why str in test function should be sending too. I'm pretty sure you know all of these.
That makes sense. I wouldn't think of it myself. Thanks!
EDIT: I should add your interpretation of sending isn't exactly how it works currently. For example, if you transferred str to another isolation in closures of test2 and test4, there would be data race. This is currently impossible because compiler doesn't allow re-send a sending value in closure (#85231).
I think you've been tripped up by the sendability of String again though — you're conflating the rules around the capture with the rules around exclusivity of access to the local variable.
If str was any non-Sendable type, including a non-Sendable value type, then the closure can't be passed to the sending parameter.
The local variable of the calling function though, can safely be mutated by the non-escaping closure. If you add @escaping to capture's argument, this won't compile even with a capture of a Sendable type.
You explanation about law of exclusivity and non-escaping closure would make sense to me if capture()'s closure parameter didn't have sending modifier. IMO sending modifier should have some impact even if it's used with a non-escaping closure. It seems weird to me that its behavior depends on whether the captured value is of reference type or value type.
I suppose you meant test1 and test3 in my report, right? That is exactly why I filed #87076. I can't see how sendability of String can explain the difference. Or let me put it in another way, are there data race in test1 and test3 in my report? No, I don't think so. IMO they don't compile only because the semantic of sending modifier and I think the same should apply if the captured value is a mutable string.
EDIT: if you remove print() statement in test1 and test3, they compile. That's by design. A closure capturing a non-Sendable instance can be passed to sending parameter if that instance is transferred together with it.