April 19

On Wednesday, 17 April 2024 at 16:51:41 UTC, Dennis wrote:

>

On Wednesday, 17 April 2024 at 14:29:17 UTC, Dukc wrote:

>

If that was the case this DIP would be completely useless, as you could already do everything it enabled with alias.

I currently sometimes use pointers to alias members.

// Inside struct LightOptimizer
void moveLightGlobal()
{
    auto light = &this.scene.light;
    light.pos += vec3(...);
}

void onMouseclick(ref GameState gs)
{
    auto ui = &gs.ui;
    ui.startDrag();
    ...
}

I'd prefer using ref since that's safer and enables opIndex overloads, so this DIP seems like a nice addition to me.

Yes I agree. I was just responding to the notion that the DIP would only allow referring to variables, not expressions. To me it doesn't seem to say that, and I'm sure the intended meaning is that expressions are fine as long as they are lvalues.

>

I tried replacing those cases with alias but it errors:

Error: accessing non-static variable `scene` requires an instance of `LightOptimizer`

Well this can be arguably seen as a "variable" as opposed to "expression", in which case I was wrong - the DIP would be of some (but much lesser) use even if it really allowed only references to variables. But no one is advocating for that restriction so it doesn't matter. At maximum the DIP writing is ambiguous on this matter.

April 19

On Friday, 19 April 2024 at 07:23:22 UTC, Nick Treleaven wrote:

>

The behaviour could perhaps be defined as follows - if this would compile:

(ref p) { CODE }(lvalue);

Then, with some restrictions on CODE, you can write:

ref p = lvalue;
CODE

And the effects will be equivalent. CODE cannot contain return statements or __traits(parameters), __traits(parent), is(T PS == __parameters) expressions. Have I missed anything else?

This formulation works IMO but it's better to just write the expression used to initialise the ref variable must be an lvalue, lvalue meaning a value with a memory address. The spec already uses "lvalue" and "rvalue" and includes them in the glossary so it's not a problem.

April 26

On Sunday, 14 April 2024 at 23:28:07 UTC, Walter Bright wrote:

>

I appreciate your thoughts.

I agree that ref parameters make it slightly less clear what is happening at the call site. But we don't really put documentation at the call site, we put the documentation at the callee. A ref parameter self-documents restrictions on how the pointer is used. out goes even further, it says that a value is not being passed in, only out. Pointers come with other behaviors - they can be incremented, and they can be free'd.

In my years of experience, I see a lot of operating system APIs that use a pointer. It's often unclear what is going to happen with that pointer. Most of the time, they stick to ref semantics, but not always. Like a char* being actually a string in C, rather than a reference to a char.

Zig tries to be explicit about how an argument is passed: It only allows pass-by-value. If you want to pass by reference, pass the address by value.

While this looks like the same mess as in C, Zig has a whole zoo of pointer types that indicate the various intentions behind a pointer. Zig distinguishes a single-element pointer and a pointer into an array; only the latter allows pointer arithmetic. Zig also distinguishes pointers into arrays that are sentinel-terminated.
A C char* that’s intended to be a string would be typed as pointer into 0-terminated array of char. A C int* that’s supposed to be ref or out would be a single-element pointer to int. Zig also has null indicators, i.e. it distinguishes pointers and slices that can be null and those that can’t be.

It’s a complete 180° from what D’s philosophy is. D style is passing a ref or a slice when you can. This means when you see a pointer, it means something more complicated is going on. One thing that I’d never do because it would confuse my future self: Passing *null via ref. It’s legal in D (not in C++), but I have never seen that being meaningfully used, i.e. I have yet to see an API that takes some ref parameter and the docs say that passing *null does X and Y.

I can see the value in both philosophies. While they seem incompatible, they’re not. D can take a concept or two from Zig.

Notably, in C#, to pass by reference, you have to put ref also before the argument in the function call. This ship has sailed for D. Also in C#, ref variables can be reassigned:

int i = 0, j = 1;
ref int r = ref i;
r = 2; // as if i = 2
r = ref j; // r points to j now
r = 3; // as if j = 3

I’m not saying D should allow reassigning ref, just pointing out that C#’s ref is quite different to D’s ref in that regard.

April 26

On Friday, 12 April 2024 at 20:43:50 UTC, Walter Bright wrote:

>

https://github.com/WalterBright/documents/blob/984374ca885e1cb10c2667cf872aebc13b4c1663/varRef.md

Why only ref?

ref on local variables is much more like ref on a parameter than a returned object. For parameters, ref is one of three: The others are in and out. Why not also include in? It seems unrelated, but it’s only slightly more general.

An in parameter is const scope, can bind rvalues, and is ref unless the type is small, then it’s a copy. That’s nothing a local variable couldn’t be and the “copy-if-small” semantic isn’t even easily mimicked.

As for out I had some ideas, but those are indeed unrelated with the DIP. (An out local variable would have to be uninitialized and have to be initialized (not assigned) before it is read. The most compelling use case is passing such a variable to a function out parameter.)

April 30
On 4/12/2024 1:43 PM, Walter Bright wrote:
> https://github.com/WalterBright/documents/blob/984374ca885e1cb10c2667cf872aebc13b4c1663/varRef.md

Implementation:

https://github.com/dlang/dmd/pull/16428
May 01
On 4/12/24 22:43, Walter Bright wrote:
> https://github.com/WalterBright/documents/blob/984374ca885e1cb10c2667cf872aebc13b4c1663/varRef.md

I am not sure how, but I had missed this thread so far. I am very much in favor, I have wished for this many times over the years. I think the only risk is implementation bugs in safety checks. The DIP seems a bit incomplete in this department, e.g., it does not say what happens when you take the address of a `ref` local. Maybe it can point to some prior art, but just stating that it cannot be returned seems neither sufficient nor necessary.

(Of course, it would be even better if fields could also be `ref`, but then you get into initialization safety. This is a general soundness problem in the current language though.)
May 01
On 5/1/2024 2:10 PM, Timon Gehr wrote:
> I am not sure how, but I had missed this thread so far. I am very much in favor, I have wished for this many times over the years. I think the only risk is implementation bugs in safety checks. The DIP seems a bit incomplete in this department, e.g., it does not say what happens when you take the address of a `ref` local. Maybe it can point to some prior art, but just stating that it cannot be returned seems neither sufficient nor necessary.

The same thing happens when you take the address of a ref parameter - you get the address of what the ref refers to.

I did my best to make it match exactly what the existing semantics of `ref` are. If I made a mistake, we can fix it when we find it.


> (Of course, it would be even better if fields could also be `ref`, but then you get into initialization safety. This is a general soundness problem in the current language though.)

`ref` is not rebindable, so a `ref` field means that support is needed in the constructor or default initializer, which needs some investigation on how to do it right.
May 01
On 4/26/2024 2:52 AM, Quirin Schroll wrote:
> Why only `ref`?

We can add more later if compelling use cases come up. Let's start with just `ref`. You're welcome to make more enhancement proposals.

May 01
I forgot to mention: ref's are not rebindable:

```
void test(int i, int j)
{
    ref int r = i; // r cannot be rebound to j
}
```
This has an interesting consequence. The lifetime of the ref will always be less than the lifetime of what it was bound to.

```
ref int r = i;
{
    int j;
    r = j; // nope, cannot bind r to j
}
```

```
int i;
{
    ref int r = i; // r has shorter lifetime than i
}
```

which makes for more safety.
May 02
On 02/05/2024 6:58 PM, Walter Bright wrote:
> I forgot to mention: ref's are not rebindable:

And this is why we should expand upon alias instead.

```d
int adder(int a, int b) {
	alias Perform = a + b;
	return Perform;
}
```

This is far more expected as a feature (I have tried to do simple aliasing of variables with it in the past).

It basically offers lazy expression inlining.

Which could be quite cool.