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