www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP71: 'noscope' and 'out!param' attributes

reply "Zach the Mystic" <reachzach gggmail.com> writes:
http://wiki.dlang.org/DIP71

I want to keep this simple. There are three ways for a reference 
passed to a function to escape that function.

static T* s;

T* fun(T* p1, T** p2) {
   // escape by global
   s = p1;

   // escape by return
   return p1;

   // escape by mutable argument
   *p2 = p1;
}

Because escape by return is by far the most common, adding 
'return' parameters as in DIP25 clearly makes a lot of sense. It 
just bugs me that the other two types of escape are still 
possible. I think they shouldn't be. DIP71 introduces two new 
attributes, "out!param" and "noscope" to address this.

I have a lot of other thoughts about this issue, but I want to 
save them for a different thread.
Jan 18 2015
next sibling parent reply "Meta" <jared771 gmail.com> writes:
On Sunday, 18 January 2015 at 18:12:57 UTC, Zach the Mystic wrote:
 http://wiki.dlang.org/DIP71

 I want to keep this simple. There are three ways for a 
 reference passed to a function to escape that function.

 static T* s;

 T* fun(T* p1, T** p2) {
   // escape by global
   s = p1;

   // escape by return
   return p1;

   // escape by mutable argument
   *p2 = p1;
 }

 Because escape by return is by far the most common, adding 
 'return' parameters as in DIP25 clearly makes a lot of sense. 
 It just bugs me that the other two types of escape are still 
 possible. I think they shouldn't be. DIP71 introduces two new 
 attributes, "out!param" and "noscope" to address this.

 I have a lot of other thoughts about this issue, but I want to 
 save them for a different thread.
If your function is marked as pure, then escape by global is impossible.
Jan 18 2015
next sibling parent "Zach the Mystic" <reachzach gggmail.com> writes:
On Sunday, 18 January 2015 at 18:17:17 UTC, Meta wrote:
 If your function is marked as pure, then escape by global is 
 impossible.
Yes, I know. Only impure functions could possibly require 'noscope'. But you might access global state without writing to it, or without copying the parameter reference. It's annoying to realize how close the existing system is to dealing with this issue.
Jan 18 2015
prev sibling parent "Zach the Mystic" <reachzach gggmail.com> writes:
On Sunday, 18 January 2015 at 18:17:17 UTC, Meta wrote:
 I have a lot of other thoughts about this issue, but I want to 
 save them for a different thread.
If your function is marked as pure, then escape by global is impossible.
Maybe it could just be made illegal to copy *any* parameter reference to a global in safe code. It does seem like a system type of thing to do anyway. Use the existing trusted attribute to deal with this one. It definitely seems rare enough to demand that the programmer mark it trusted. I'm not sure, though. Basically, trusted is the blunt instrument, 'noscope' is the fine one.
Jan 18 2015
prev sibling next sibling parent "Zach the Mystic" <reachzach gggmail.com> writes:
In general, these attributes would only appear very rarely.
Jan 18 2015
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
Some random comments:

I like that it applies to all kinds of references, not just 
`ref`. Do you want it to apply to structures with reference 
members, too? What about value types in general?

About `out!`: I think this should be placed next to the escaping 
parameter for consistency (i.e. `out!p2 T* p1` in your example), 
because all other annotations are already at the parameters that 
escape.

Instead of `noscope`, I suggest `static`, because that's already 
a keyword and will not clash with existing code. (Note that this 
can the apply to `this`, and `static` then needs to be placed 
behind the function to distinguish it from a static method 
declaration, where it appears in front.)

It's a really interesting idea to mark distinguish the different 
ways of escaping. This might have further implications, in 
particular in relation to purity.

How does this proposal interact with `scope`? It seems you want 
the compiler to track lifetimes for all reference parameters, 
even those not marked as `scope`. At least your example doesn't 
use `scope`.

Apart from that, I'll have to think about a few things. For 
example, I don't know yet whether and how a safe owning type/RC 
can be implemented with this.
Jan 18 2015
parent "Zach the Mystic" <reachzach gggmail.com> writes:
On Sunday, 18 January 2015 at 19:05:37 UTC, Marc Schütz wrote:
 Some random comments:

 I like that it applies to all kinds of references, not just 
 `ref`. Do you want it to apply to structures with reference 
 members, too? What about value types in general?
I have some thoughts about `ref` that I will say at a different time. I think any structure with a reference member must be treated like a reference type. Value types with no references will certainly never require these attributes. I'm not sure if it should be an error to add them or not. Probably not.
 About `out!`: I think this should be placed next to the 
 escaping parameter for consistency (i.e. `out!p2 T* p1` in your 
 example), because all other annotations are already at the 
 parameters that escape.
I would disagree here. Consistency for its own sake isn't convincing. The outgoing parameter (p2) will probably be less cluttered and further to the right, enhancing overall readability. But more importantly, only the users of p2 will need to know that it's special (i.e., that its scope is restricted to that of p1). The relevant information is kept close to the place it is required. Makes sense?
 Instead of `noscope`, I suggest `static`, because that's 
 already a keyword and will not clash with existing code. (Note 
 that this can the apply to `this`, and `static` then needs to 
 be placed behind the function to distinguish it from a static 
 method declaration, where it appears in front.)
Interesting suggestion! You might be right. I should tell you how I got to `noscope`. There's a fourth way a reference can escape, which clouded my thinking such that I didn't include it in the DIP, because it wasn't technically necessary. But now, I'm trying to figure it out again. In particular, let's call it escaping by heap. T** fun(return T* t) { T** u = new T*; *u = t; return u; } T** gun() { T v; T* w = &v; return fun(w); // no good, despite heap allocation } I had thought that a new heap variable was the equivalent of a global variable, because both have infinite lifetime. Hence, `noscope` rather than `global`, for example, or `heap`. But now I'm realizing that when a reference parameter (i.e. `t` above) escapes either by return or by mutable parameter, the escape must *always* be considered to have the same scope as the calling parameter. The `new T*` in `fun()` may be a heap variable, but it points to a stack variable in `gun()` just the same and thus must be treated by `gun()` as such. Nevertheless, as I think further, I realize that there's nothing wrong with passing a heap variable *to* a `noscope` parameter, because it's not unsafe for the static data segment to point to the heap, as they are coordinated by the garbage collector. Therefore, the reason to use `noscope` would be to indicate that anything which has not been allocated on the stack may be *passed* to the function, while calling it `static` would merely indicate where the parameter might be copied *to* once inside the function. As far as adding a new keyword, I think it depends on the keyword. `noscope` isn't so bad because it fits into the `throw/nothrow` pattern already. I think the most important question is whether anyone is normally tempted to use the keyword as a variable name. I consider the `body` keyword, for example, regrettable. But do you think anyone will shed a tear for `noscope`? I'm not worried about it.
 How does this proposal interact with `scope`? It seems you want 
 the compiler to track lifetimes for all reference parameters, 
 even those not marked as `scope`. At least your example doesn't 
 use `scope`.
You're right. In my head there is a scheme for doing this. I'll present it in due time.
 Apart from that, I'll have to think about a few things. For 
 example, I don't know yet whether and how a safe owning type/RC 
 can be implemented with this.
I don't either. I am pretty sure, however, that this will not harm the effort. All these attributes do is give the compiler more information to work with. In the back of my mind, I'm always thinking systems programmers can always trust whatever doesn't work for them, although I still can't think of any use cases for violating these principles.
Jan 18 2015