July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Pfau | On Wed, Jul 09, 2014 at 11:33:09PM +0200, Johannes Pfau via Digitalmars-d wrote: [...] > For delegates scope can prevent closure heap allocation. For all other types it does nothing. Example: > > import std.stdio; > > void testA(void delegate() cb) > { > cb(); > } > void testB(scope void delegate() cb) > { > cb(); > } > > void main() > { > int a; > void callback() {a = 42;} > //Callback accesses a, testA might store a reference to callback > //->a might be accessible after this main function returns > //->can't keep it on the stack. Allocate a on the heap > testA(&callback); > > //Callback accesses a, but testB does not store a reference > //as it tells us by using scope > //So as soon as testB returns, there's no reference to a floating > //around and we can allocate a on the stack. > //(Of course as long as we call testA in this function, a is always on > // the heap. but if we only call testB it can be on the stack) > testB(&callback); > } Unfortunately, it seems that this is not enforced by the compiler at all. For example: int delegate() globDg; void func(scope int delegate() dg) { globDg = dg; // shouldn't compile, but does globDg(); } void sub() { int x; func(() { return ++x; }); // oops } void trashme() { import std.stdio; writeln(globDg()); // prints garbage } void main() { sub(); trashme(); } If 'scope' is commented out, then it works as expected (i.e., x gets allocated on the heap). https://issues.dlang.org/show_bug.cgi?id=13085 T -- Dogs have owners ... cats have staff. -- Krista Casada |
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On Wednesday, 9 July 2014 at 20:58:11 UTC, Sean Kelly wrote:
> On Wednesday, 9 July 2014 at 19:47:02 UTC, Walter Bright wrote:
>> On 7/9/2014 3:20 AM, Dicebot wrote:
>>>
>>> I'd state it differently: "Marketing fuss about goroutines is the killer feature
>>> of Go" :) It does not have any fundamental advantage over existing actor model
>>> and I doubt it will matter _that_ much.
>>
>> Much of the froth about Go is dismissed by serious developers, but they nailed the goroutine thing. It's Go's killer feature.
>
> I think it still mostly comes down to marketing :-) That said,
> we're pretty close in D once my Scheduler pull request is
> accepted. Between that and the Generator type in the same pull
> request, we're not missing much but the weird switch syntax they
> have. There's still some work to do, but I think a lot of it
> really comes down to showing people what's already possible in D
> rather than writing more code.
Might also be a win to make it _visible_ in the language as a first-class construct by also adding async/await syntax sugar.
Joseph
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 7/9/2014 2:47 PM, Andrei Alexandrescu wrote:
> On 7/9/14, 1:51 PM, Walter Bright wrote:
>> On 7/9/2014 1:35 PM, Andrei Alexandrescu wrote:
>>> Hmmm... how about using u after that?
>>
>> Using u after that would either cause an exception to be thrown, or
>> they'd get T.init as a value. I tend to favor the latter, but of course
>> those decisions would have to be made as part of the design of Unique.
>
> That semantics would reenact the auto_ptr disaster so probably wouldn't be a
> good choice. -- Andrei
>
Is there a good reference on that disaster?
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | On 7/9/2014 1:42 PM, bearophile wrote:
> Is it possible & useful & good to put something like a "@not_null_references:"
> (or a similar pragma) at the top of a module? How are differently defaulting
> modules going to interact with each other?
Exactly. I'm not seeing how this can work that well.
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 7/9/2014 1:58 PM, Sean Kelly wrote:
> I think it still mostly comes down to marketing :-) That said,
> we're pretty close in D once my Scheduler pull request is
> accepted. Between that and the Generator type in the same pull
> request,
Ah, I didn't know you had a PR request already! Awesome!
> we're not missing much but the weird switch syntax they have.
I'm not concerned about the syntax. Any syntax we use should be "D style" syntax, not "Go style".
> There's still some work to do, but I think a lot of it
> really comes down to showing people what's already possible in D
> rather than writing more code.
I agree.
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 7/9/2014 1:58 PM, Sean Kelly wrote: > we're pretty close in D once my Scheduler pull request is > accepted. I couldn't find it. Can you please add the link to the PR to: https://issues.dlang.org/show_bug.cgi?id=13086 ? |
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 7/9/14, 3:25 PM, Walter Bright wrote: > On 7/9/2014 2:47 PM, Andrei Alexandrescu wrote: >> On 7/9/14, 1:51 PM, Walter Bright wrote: >>> On 7/9/2014 1:35 PM, Andrei Alexandrescu wrote: >>>> Hmmm... how about using u after that? >>> >>> Using u after that would either cause an exception to be thrown, or >>> they'd get T.init as a value. I tend to favor the latter, but of course >>> those decisions would have to be made as part of the design of Unique. >> >> That semantics would reenact the auto_ptr disaster so probably >> wouldn't be a >> good choice. -- Andrei >> > > Is there a good reference on that disaster? https://www.google.com/search?q=std%20auto_ptr%20sucks&gws_rd=ssl :o) It was the one library artifact that was deemed sufficiently bad for C++ to be straight deprecated. They didn't attempt that even with iostreams. Andrei |
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Joseph Cassman | On 7/9/2014 3:24 PM, Joseph Cassman wrote:
> Might also be a win to make it _visible_ in the language as a first-class
> construct by also adding async/await syntax sugar.
async/await are great ideas, but are something quite different from goroutines.
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
On 07/10/14 00:16, H. S. Teoh via Digitalmars-d wrote:
> On Wed, Jul 09, 2014 at 11:33:09PM +0200, Johannes Pfau via Digitalmars-d wrote: [...]
>> For delegates scope can prevent closure heap allocation. For all other types it does nothing. Example:
>>
>> import std.stdio;
>>
>> void testA(void delegate() cb)
>> {
>> cb();
>> }
>> void testB(scope void delegate() cb)
>> {
>> cb();
>> }
>>
>> void main()
>> {
>> int a;
>> void callback() {a = 42;}
>> //Callback accesses a, testA might store a reference to callback
>> //->a might be accessible after this main function returns
>> //->can't keep it on the stack. Allocate a on the heap
>> testA(&callback);
>>
>> //Callback accesses a, but testB does not store a reference
>> //as it tells us by using scope
>> //So as soon as testB returns, there's no reference to a floating
>> //around and we can allocate a on the stack.
>> //(Of course as long as we call testA in this function, a is always on
>> // the heap. but if we only call testB it can be on the stack)
>> testB(&callback);
>> }
>
> Unfortunately, it seems that this is not enforced by the compiler at all.
Yes, scope is not enforced at all.
Also:
void main()
{
int a;
auto callback = {a = 42;}; // heap alloc
scope callback = {a = 42;}; // stack (ie normal) alloc
testA(callback);
testB(callback);
}
Then there are lazy args, which are basically scope delegates, but with no way to turn off 'scope' and no escape protection.
Trying to tack 'borrowed' on 'ref' is not a good idea. (would require new restrictions, hence not backwards compatible; it can't be a @safe-only thing, `borrowed` affects lifetimes etc)
artur
|
July 09, 2014 Re: Opportunities for D | ||||
---|---|---|---|---|
| ||||
On Wed, Jul 09, 2014 at 03:16:37PM -0700, H. S. Teoh via Digitalmars-d wrote: [...] > https://issues.dlang.org/show_bug.cgi?id=13085 [...] Hmm, apparently, this is a long-standing known issue: https://issues.dlang.org/show_bug.cgi?id=5270 Judging from this, a big missing piece of the current implementation is the actual enforcement of 'scope'. So here's a first stab at refining (and extending) what 'scope' should be: - 'scope' can be applied to any variable, and is part of its type. Let's call this a "scoped type", and a value of this type a "scoped value". - Every scoped type has an associated lifetime, which is basically the scope in which it is declared. - The lifetime of a scoped variable is PART OF ITS TYPE. - An unscoped variable is regarded to have infinite lifetime. - For function parameters, this lifetime is the scope of the function body. - For local variables, the lifetime is the containing lexical scope where it is declared. - Taking the address of a scoped value returns a scoped pointer, whose lifetime is the lexical scope where the address-of operator is used. - A scoped type can only be assigned to another scoped type of identical or narrower lifetime. Basically, the idea here is that a scoped value can only have its scope narrowed, never expanded. In practice, this means: - If a scoped type is a reference type (class or pointer or ref), it can only be assigned to another scoped type whose associated lifetime is equal or contained within the source value's associated lifetime. - If a scoped type is a value type with indirections, it can only be assigned to an lvalue of the same scoped type (with the same associated lifetime). - If a scoped type is a value type with no indirections, it's freely assignable to a non-scoped lvalue of compatible type. - A function's return type can be scoped (not sure what syntax to use here, since it may clash with scope delegates). - The lifetime of the return value is the containing scope of the function definition -- if it's a module-level function, then its lifetime is infinite. If it's an inner function, then its lifetime is the containing lexical scope of its definition. Example: class C {} void func() { // return type of helper is scope(C) with lifetime up to // the end of func's body. scope(C) helper() { ... } } - Returning a value from a function is considered to be equivalent to assigning the value to a variable of the return type of the function. Thus: class C {} void func() { scope(C) c1; // helper's return type has lifetime == func's body scope(C) helper() { scope(C) c2; if (cond) return c1; // OK, c1's lifetime == func's body else return c2; // ILLEGAL: c2's lifetime < func's body } } - Since a scoped return type has its lifetime as part of its type, the type system ensures that scoped values never escape their lifetime. For example, if we are sneaky and return a pointer to an inner function, the type system will prevent leakage of the scoped value: class C {} auto func() { scope(C) c1; // Return type of sneaky is scope(C) with lifetime = // body of func. scope(C) sneaky() { // This is OK, because c1's lifetime == body of // func, which is compatible with its return type. return c1; } // Aha! we now we have broken scope... or have we? return &sneaky; } void main() { // Let's see. Get a function pointer to a function that // "leaks" a scoped value... auto funcptr = func(); // But this doesn't compile, because the return type of // funcptr() is a scoped variable whose lifetime is // inside the body of func(), but since we're outside of // func here, the lifetime of x doesn't match the // lifetime of funcptr()'s return value, so the // following assignment is rejected as having // incompatible types: auto x = funcptr(); // This will actually work... but it's OK, because we // aren't actually storing the return value of // funcptr(), so the scoped value is actually not leaked // after all. funcptr(); } - Aggregates: - It's turtles all the way down: members of scoped aggregates also have scoped type, with lifetime inherited from the parent aggregate. In other words, the lifetime of the aggregate is transitive to the lifetime of its members. For example: class C {} struct S { C c; int x; } int func(scope S s) { // N.B. lifetime of s is func's body. auto c1 = s.c; // typeof(c1) == scope(C) with lifetime = func's body C d; d = c1; // illegal: c1 has shorter lifetime than d. return s.x; // OK, even though typeof(s.x) has lifetime = // func's body, it's a value type so we're // actually copying it to func's return value, // not returning the actual scoped int. } - Passing parameters: since unscoped values are regarded to have infinite lifetime, it's OK to pass unscoped values into scoped function parameters: it's a narrowing of lifetime of the original value, which is allowed. (What's not allowed is expanding the lifetime of a scoped value.) I'm sure there are plenty of holes in this proposal, so destroy away. ;-) T -- If I were two-faced, would I be wearing this one? -- Abraham Lincoln |
Copyright © 1999-2021 by the D Language Foundation