Thread overview
DIP71: 'noscope' and 'out!param' attributes
Jan 18, 2015
Zach the Mystic
Jan 18, 2015
Meta
Jan 18, 2015
Zach the Mystic
Jan 18, 2015
Zach the Mystic
Jan 18, 2015
Zach the Mystic
Jan 18, 2015
Marc Schütz
Jan 18, 2015
Zach the Mystic
January 18, 2015
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.
January 18, 2015
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.
January 18, 2015
In general, these attributes would only appear very rarely.
January 18, 2015
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.
January 18, 2015
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.
January 18, 2015
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.
January 18, 2015
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.