On 06/08/12 06:03, Steven Schveighoffer wrote:
> On Thu, 07 Jun 2012 20:58:13 -0400, Artur Skawina <art.08...@gmail.com> wrote:
> 
>> On 06/08/12 01:51, Steven Schveighoffer wrote:
>>>
>>> The following would be illegal:
>>>
>>> struct X
>>> {
>>>   shared int x; // illegal
>>>   shared(int)* y; // legal
>>>
>>>   shared(X) *next; // legal
>>> }
>>
>> Note that the type of 'x' in
>>
>>    shared struct S {
>>       int x;
>>    }
>>
>> should probably be 'shared(int)'.
>> Which lets you safely take an address of an aggregates field.
>
> That's one of the things I'm wondering about.  Should it be allowed?

Must be. We're talking about the next generation of a high level assembler,
not logo. :)


> 
> I agree that the type should be shared(int), but the type should not transfer 
> to function calls or auto, it should be sticky to the particular variable.  
> Only references should be sticky typed.

The problem with this is that it should be symmetrical, IOW the conversion
from non-shared to shared would also have to be (implicitly) allowed.
A type that converts to both would be better, even if harder to implement.

>> And I'm not sure if marking a struct and class as shared would work
>> correctly right now, it's probably too easy to lose the 'shared' qualifier.
> 
> Right, I was thinking shared structs do not make sense, since I don't think 
> shared members do not make sense.  Either a whole struct/class is shared or 
> it is not.  Because you can only put classes on the heap, shared makes sense 
> as an attribute for a class.
> 
> But then again, it might make sense to say "this struct is only ever shared, 
> so it should be required to go on the heap".  I like your idea later about 
> identifying shared struct types that should use synchronization.

Of course shared structs make sense, it's what allows implementing any
non-trivial shared type. 

   static Atomic!int counter;

inside a function is perfectly fine. And, as somebody already mentioned
in this thread, omitting 'static' should cause a build failure; right
now it is accepted, even when written as

   shared Atomic!int counter;

The problem? 'shared' is silently dropped. Move the counter from a struct
into a function after realizing it's only accessed from one place, forget
to add 'static' - and the result will compile w/o even a warning. 

> 
>>> If you notice, I never allow shared values to be stored on the stack, they 
>>> are always going to be stored on the heap.  We can use this to our 
>>> advantage -- using special allocators that are specific to shared data, we 
>>> can ensure the synchronization tools necessary to protect this data gets 
>>> allocated on the heap along side it.  I'm not sure exactly how this could 
>>> work, but I was thinking, instead of allocating a monitor based on the 
>>> *type* (i.e. a class), you allocate it based on whether it's *shared* or 
>>> not.  Since I can never create a shared struct X on the stack, it must be 
>>> in the heap, so...
>>>
>>> struct X
>>> {
>>>    int y;
>>> }
>>>
>>> shared(X) *x = new shared(X);
>>>
>>> synchronized(x) // scope-locks hidden allocated monitor object
>>> {
>>>    x.y = 5;
>>> }
>>>
>>> x.y = 5; // should we disallow this, or maybe even auto-lock x?
>>>
>>> Hm... another idea -- you can't extract any piece of an aggregate.  That 
>>> is, it would be illegal to do:
>>>
>>> shared(int)* myYptr = &x.y;
>>>
>>> because that would work around the synchronization.
>>
>> That's too restrictive. It would overload 'shared' even more. If you
>> want that kind of synchronize magic to work, just allow:
>>
>>    shared synchronized(optional_locking_primitive) struct S {
>>       ...
>>    }
>>
>> And *now* 'x.y = 5' can do its magic, while '&x.y' can be disallowed.
>>
>>
>>    shared struct S {
>>       Atomic!int i;
>>    }
>>    shared(S)* p = ...
>>    p.i += 1;
>>
>> should work, so accessing fields must remain possible.
> 
> OK.  You are right, synchronized may be overkill for basic types.
> 
>>> auto would have to strip shared:
>>>
>>> auto myY = x.y; // typeof(myY) == int.
>>
>> Hmm, i'm not sure about this, maybe it should be disallowed; it could
>> only strip 'shared' from the head anyway.
> 
> Yes, head stripping.  I think it should be allowed.  For instance, if you 
> wanted to read a shared double, and take the cosine of it, this should be 
> allowed:
> 
> auto n = cos(sharedValue);
> 
> If it's not, the alternatives are:
> 
> auto n = cos(cast()sharedValue);
> 
> or
> 
> double v = sharedValue; // explicit removal of shared
> auto m = cos(v);
> 
> Neither of these look necessary, I think just allowing shared value types to 
> automatically convert to non-shared versions works the best.

If 'shared(VT)' implicitly converts to VT, then

   auto myY = x.y; // typeof(myY) == shared(int)

would still be fine. So would

  auto n = cos(x,y); // assuming some weird cos() that works on ints ;)

But I'm not sure allowing these implicit conversions is a good idea.
At least not yet. :)

>>> This is definitely not a complete proposal.  But I wonder if this is the 
>>> right direction?
>>
>> I think it is.
> 
> good.

It's a small step in the right direction.

artur

Reply via email to