October 01, 2018
On 10/1/18 7:09 PM, Manu wrote:
> On Mon, Oct 1, 2018 at 8:55 AM Timon Gehr via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>>
>> On 01.10.2018 04:29, Manu wrote:
>>> struct Bob
>>> {
>>>     void setThing() shared;
>>> }
>>>
>>> As I understand, `shared` attribution intends to guarantee that I dun
>>> synchronisation internally.
>>> This method is declared shared, so if I have shared instances, I can
>>> call it... because it must handle thread-safety internally.
>>>
>>> void f(ref shared Bob a, ref Bob b)
>>> {
>>>     a.setThing(); // I have a shared object, can call shared method
>>>
>>>     b.setThing(); // ERROR
>>> }
>>>
>>> This is the bit of the design that doesn't make sense to me...
>>> The method is shared, which suggests that it must handle
>>> thread-safety. My instance `b` is NOT shared, that is, it is
>>> thread-local.
>>> So, I know that there's not a bunch of threads banging on this
>>> object... but the shared method should still work! A method that
>>> handles thread-safety doesn't suddenly not work when it's only
>>> accessed from a single thread.
>>> ...
>>
>> shared on a method does not mean "this function handles thread-safety".
>> It means "the `this` pointer of this function is not guaranteed to be
>> thread-local". You can't implicitly create an alias of a reference that
>> is supposed to be thread-local such that the resulting reference can be
>> freely shared among threads.
> 
> I don't understand. That's the point of `scope`... is that it won't
> escape the reference. 'freely shared' is the antithesis of `scope`.
> 
>>> I feel like I don't understand the design...
>>> mutable -> shared should work the same as mutable -> const... because
>>> surely that's safe?
>>
>> No. The main point of shared (and the main thing you need to understand)
>> is that it guarantees that if something is _not_ `shared` is is not
>> shared among threads. Your analogy is not correct, going from
>> thread-local to shared is like going from mutable to immutable.
> 
> We're talking about `mutable` -> `shared scope`. That's like going
> from mutable to const.
> `shared scope` doesn't say "I can share this", what it says is "this
> may be shared, but *I won't share it*", and that's the key.
> By passing a thread-local as `shared scope`, the receiver accepts that
> the argument _may_ be shared (it's not in this case), but it will not
> become shared in the call. That's the point of scope, no?
> 
>> If the suggested typing rule was implemented, we would have the
>> following way to break the type system, allowing arbitrary aliasing
>> between mutable and shared references, completely defeating `shared`:
>>
>> class C{ /*...*/ }
>>
>> shared(C) sharedGlobal;
>> struct Bob{
>>       C unshared;
>>       void setThing() shared{
>>           sharedGlobal=unshared;
>>       }
>> }
>>
>> void main(){
>>       C c = new C(); // unshared!
>>       Bob(c).setThing();
>>       shared(D) d = sharedGlobal; // shared!
>>       assert(c !is d); // would fail (currently does not even compile)
>>       // sendToOtherThread(d);
>>       // c.someMethod(); // (potential) race condition on unshared data
>> }
> 
> Your entire example depends on escaping references. I think you missed
> the point?
> 

The problem with mutable wildcards is that you can assign them.

This exposes the problem in your design. The reason const works is because you can't mutate it. Shared is not the same.

simple example:

void foo(scope shared int *a, scope shared int *b)
{
   a = b;
}

If I can bind a to a local mutable int pointer, and b as a pointer to global shared int, the assignment is now considered OK (types and scopes are the same), but now my local points at a shared int without the shared adornments.

The common wildcard you need between shared and mutable is *unique*. That is, even though it's typed as shared or unshared, the compiler has guaranteed there is no other reference to that data. In that case, you can move data from one place to another without compromising the system (as you assign from one unique pointer to another, the original must have to be nullified, otherwise the wildcard still would not work, and the unique property would cease to be accurate).

IMO, the correct way to deal with shared would be to make it 100% unusable. Not readable, or writable. And then you have to cast away shared to make it work (and hopefully performing the correct locking to make sure your changes are defined). I don't think there's a magic bullet that can fix this.

-Steve
October 01, 2018
On 10/1/18 7:56 PM, Steven Schveighoffer wrote:
> On 10/1/18 7:09 PM, Manu wrote:
>> Your entire example depends on escaping references. I think you missed
>> the point?
>>
> 
> The problem with mutable wildcards is that you can assign them.
> 
> This exposes the problem in your design. The reason const works is because you can't mutate it. Shared is not the same.
> 
> simple example:
> 
> void foo(scope shared int *a, scope shared int *b)
> {
>     a = b;
> }

Haha, of course, this has no effect!

In order for it to show the problem, a has to be ref'd.

-Steve
October 01, 2018
On 10/1/2018 4:56 PM, Timon Gehr wrote:
> There was no 'scope' in the OP, and no, that is not sufficient either, because scope is not transitive but shared is.

Oops, I missed that point. Glad you noticed it.
October 01, 2018
On Mon, Oct 1, 2018 at 5:00 PM Timon Gehr via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 02.10.2018 01:09, Manu wrote:
> > Your entire example depends on escaping references. I think you missed the point?
>
> There was no 'scope' in the OP, and no, that is not sufficient either, because scope is not transitive but shared is.

Surely `scope` must be transitive? How could it work otherwise?
October 02, 2018
On 10/1/2018 7:31 PM, Manu wrote:
> Surely `scope` must be transitive?

It isn't.

> How could it work otherwise?

It's a storage class, not a type constructor. There is no "pointer to scope" type, for example. Having it transitive would make it unworkable, actually, for similar reasons that transitive const makes some uses of C++ const unworkable in D.

October 02, 2018
On Tue, Oct 2, 2018 at 12:45 AM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 10/1/2018 7:31 PM, Manu wrote:
> > Surely `scope` must be transitive?
>
> It isn't.
>
> > How could it work otherwise?
>
> It's a storage class, not a type constructor. There is no "pointer to scope"
> type, for example. Having it transitive would make it unworkable, actually, for
> similar reasons that transitive const makes some uses of C++ const unworkable in D.

So... `scope` says "I won't escape this, but I may escape anything this points to"?
October 02, 2018
On 10/2/2018 1:49 PM, Manu wrote:
> So... `scope` says "I won't escape this, but I may escape anything
> this points to"?

That's right.

http://dconf.org/2017/talks/bright.html

October 03, 2018
On Tuesday, 2 October 2018 at 21:35:40 UTC, Walter Bright wrote:
> On 10/2/2018 1:49 PM, Manu wrote:
>> So... `scope` says "I won't escape this, but I may escape anything
>> this points to"?
>
> That's right.
>
> http://dconf.org/2017/talks/bright.html

I'm confused. Given how the lifetimes of aggregates are defined in DIP1000, and also given that I tried to escape members of a struct when I wrote fearless and the compiler didn't let me, I'm trying to understand in what situation scope doesn't apply transitively.
October 03, 2018
On 10/3/2018 1:33 AM, Atila Neves wrote:
> I'm confused. Given how the lifetimes of aggregates are defined in DIP1000, and also given that I tried to escape members of a struct when I wrote fearless and the compiler didn't let me, I'm trying to understand in what situation scope doesn't apply transitively.

for 'scope T**' the scope applies to the T**, but not to the T*.
October 04, 2018
On Tuesday, 2 October 2018 at 07:42:13 UTC, Walter Bright wrote:
> Having it transitive would make it unworkable, actually

What would be broken by transitive scope?
1 2 3
Next ›   Last »