August 28
Wed, 28 Aug 2024 at 21:16, Nick Treleaven via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 28 August 2024 at 10:41:25 UTC, Manu wrote:
> > On Wed, 28 Aug 2024 at 19:46, Nick Treleaven via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> >> > struct T
> >> > {
> >> >   S s;
> >> >   int i;
> >> >
> >> >   alias a = i; // works
> >> >   alias b = s.x; // Doesn't work? Why?
> >> > }
> >>
> >> `a` is a symbol alias. `b` would be an expression if it did what you want. Instead it's the same as `S.x`, which needs an instance of S to use it at runtime.
> >>
> >
> > Not necessarily; it doesn't need to carry around the expression; it could evaluate the resolve the expression on the spot.
>
> It resolves to the field declaration `x`, which knows nothing about `s` - because `s.x` is not a declaration.


Right... that's that bug. And needs to be fixed.


It might confuse
> the concept of aliases if you allow them to refer to some sub-part of a declaration.
>

Definitely no; it will distinctly UN-confuse the concept of aliases.
It's been requested endlessly since forever, it's obviously how people
expect it to work.
Everyone I've introduced D to has found themself here in the first few
days, and it just messes everything up.
The wonky edge cases and work-arounds are a clear sign it's all gone off
the rails.


You could instead write:
> ```d
> ref b() => s.x;
> ```
>

Not the same thing at all... it's also an admission of defeat, and makes D
look broken.
The path of exploration that leads to this hack is a really bad experience,
and it does not do us any favours in terms of optics, or language
satisfaction.


>> `tupleof` is a symbol sequence of implicit ref declarations.
> >>
> >
> > What's a 'ref'?
>
> https://dlang.org/changelog/pending.html#dmd.reflocal
>

Yeah, I can't wait for the next release to land; we've been waiting
literally forever for this!
That said, it doesn't really answer my question; what IS a ref? Nobody
really knows; it's not really a thing that the language can properly
express.

For instance, the evidence here: __traits(getParamterStorafeClasses,
function, paramterByIntegerIndex)
How would you expect to detect if something is ref, or scope, or whatever?
Of course, what you expect is a STRING LITERAL, which you can compare to a
string of the name of the storage class.
There's no stronger evidence that storage classes are themselves such a
broken idea that exist way outside of the language than passing around
string literals to tell you a fact about some declaration.
C++ puts these things in the type, and while that creates some awkward
cases, it's infinitely better than what we have in D in terms of
expressiveness and flexibility in terms of meta programming. Ref as part of
a type means it can participate in type-like expressions, inferences, etc.

What's kinda funny, is that your link to the cool ref-local feature (finally!) above will be shortly followed with "how do I detect if a local is a reference?" ... there's no __traits(getLocalVariableStorageClass, ...)

So I still don't really know; what IS a ref? It's astonishing that s.tupleof is a sequence of ref's as you say... does anything else in the language have a semantic like that? How could I synthesise something like that from anything else if I wanted to?


August 28
On Wednesday, 28 August 2024 at 11:47:12 UTC, Manu wrote:
> For instance, the evidence here: __traits(getParamterStorafeClasses,
> function, paramterByIntegerIndex)
> How would you expect to detect if something is ref, or scope, or whatever?

__traits(isRef, x)

I expect a trait for isScope could be added if needed.

> Of course, what you expect is a STRING LITERAL, which you can compare to a
> string of the name of the storage class.
> There's no stronger evidence that storage classes are themselves such a
> broken idea that exist way outside of the language than passing

Just because you can't e.g. alias a storage class doesn't mean it is 'outside' the language. It's part of a declaration.

> around
> string literals to tell you a fact about some declaration.
> C++ puts these things in the type, and while that creates some awkward
> cases, it's infinitely better than what we have in D in terms of
> expressiveness and flexibility in terms of meta programming.

I've heard that C++ references are a complete swamp of special cases (regardless of lvalue vs rvalue reference).

> Ref as part of
> a type means it can participate in type-like expressions, inferences, etc.

I think its better to explicitly write `ref` if you want a ref, rather than infer it.

> What's kinda funny, is that your link to the cool ref-local feature (finally!) above will be shortly followed with "how do I detect if a local is a reference?" ... there's no __traits(getLocalVariableStorageClass, ...)

__traits(isRef, x)

Example with `auto ref` in the changelog.

> So I still don't really know; what IS a ref?

It's a pointer that is automatically dereferenced on use, and never does pointer arithmetic.

> It's astonishing that s.tupleof is a sequence of ref's as you say...

BTW that was just how I understand `.tupleof`. I see it as (a superset of) a sequence of implicit ref declarations.

> does anything else in the language have a semantic like that?
> How could I synthesise something like that from anything else if I wanted to?

If you mean synthesize `tupleof`, I already showed how (without introspection support), the first example here:
https://forum.dlang.org/post/fctetiyhbiyhadlmcyzr@forum.dlang.org

That example literally works with dmd recent git.
August 28

On Sunday, 25 August 2024 at 19:40:48 UTC, Timon Gehr wrote:

>

FWIW with my frontend this just works:

I've always wished there'd be a way for a nested struct to access its parent context. Can your frontend handle something like this:

struct S2(alias instance)
{
    alias Outer = __traits(parent, instance);
    ref Outer outer() => *cast(Outer*)(cast(void*)&this - instance.offsetof);
}

struct S
{
    S2!inner inner;
}

?

There's a workaround using mixins, but it's not great:

mixin template S2(string name)
{
    alias Outer = typeof(this);

    struct S2
    {
        ref Outer outer() => *cast(Outer*)(cast(void*)&this - field.offsetof);
        void foo()
        {
            import std.stdio;
            writeln(name, ": outer.x = ", outer.x);
        }
    }

    mixin("S2 ", name, ";");
    mixin("alias field = ", name, ";");
}

struct S
{
    int x;
    mixin S2!"inner";
}

void main()
{
    S s = S(42);
    s.inner.foo();
}
August 28

On Wednesday, 28 August 2024 at 16:37:12 UTC, Max Samukha wrote:

>
    ref Outer outer() => *cast(Outer*)(cast(void*)&this - field.offsetof);

Looks like the name can be mixed-in directly:

ref Outer outer() => *cast(Outer*)(cast(void*)&this - mixin(name).offsetof);
August 29
On Thu, 29 Aug 2024 at 01:21, Nick Treleaven via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 28 August 2024 at 11:47:12 UTC, Manu wrote:
> > For instance, the evidence here:
> > __traits(getParamterStorafeClasses,
> > function, paramterByIntegerIndex)
> > How would you expect to detect if something is ref, or scope,
> > or whatever?
>
> __traits(isRef, x)
>
> I expect a trait for isScope could be added if needed.
>

Did you miss my point? The language can't *express *these things... we need backdoor information to hold the concept in any sort of expression.

> Of course, what you expect is a STRING LITERAL, which you can
> > compare to a
> > string of the name of the storage class.
> > There's no stronger evidence that storage classes are
> > themselves such a
> > broken idea that exist way outside of the language than passing
>
> Just because you can't e.g. alias a storage class doesn't mean it is 'outside' the language. It's part of a declaration.
>

You didn't miss my point. Hmmm.

> around
> > string literals to tell you a fact about some declaration.
> > C++ puts these things in the type, and while that creates some
> > awkward
> > cases, it's infinitely better than what we have in D in terms of
> > expressiveness and flexibility in terms of meta programming.
>
> I've heard that C++ references are a complete swamp of special cases (regardless of lvalue vs rvalue reference).
>

You've "heard"? I'm sorry, but your qualification to comment on the topic is definitely problematic from here on...

Anyone who holds the opinion that C++ references are a swamp of special
cases *by contrast *has clearly not had any comparable experience with D
references (and additional *suite* of storage classes). Nobody with
experience in this matter can honestly say with a straight face that C++ is
complicated and edgy by direct comparison.
Nothing in D is more awkward or poorly expressed.

> What's kinda funny, is that your link to the cool ref-local
> > feature (finally!) above will be shortly followed with "how do
> > I detect if a local is a reference?" ... there's no
> > __traits(getLocalVariableStorageClass, ...)
>
> __traits(isRef, x)
>
> Example with `auto ref` in the changelog.
>

Okay, fair; I had my mind on the general query relating to storage class
like the one that returns a list of strings for each parameter. But it's
like you say "one could be added for scope"; which actually demonstrates my
point equally well.
... and why do we have 2 ways that do exactly the same thing?

C++ just doesn't have any of this nonsense. My biggest sense of loss from C++ to D is this fundamental area of the language in D that exists outside the language, and requires hacks and general weird-shit to access.

> So I still don't really know; what IS a ref?
>
> It's a pointer that is automatically dereferenced on use, and never does pointer arithmetic.
>

No, that's what it *does*. Write me an expression that embodies the
concept? Write me an expression that copies ref-ness from one declaration
to another declaration, for instance; I might be synthesising a parameter
list for a shim with information from a given function...
It can't be expressed in the language. It's just an internal compiler
concept that's unbelievably awkward to reason about, and can only be
reasoned with back-door information.

Have you had a look at core.lifetime recently? Take a look at C++'s
implementation of `std::forward`, `std:: move`, `emplace` (aka placement
new)... tell me the situation in D is superior... core.lifetime is a war
zone; you'll get a really good signal of just how far off the mark we have
landed when comparing those fundamental language primitives.
There's basically nothing lower in the concept stack than core.lifetime; so
why is it a complete mess? The reason is because none of our core concepts
fit together; D is made out of parts with seriously jagged edges, and it
needs a flood of hacky special-case reasoning to resolve the problems at
the lowest level.

D's advantage is that a lot of things ARE POSSIBLE, but they come at a massive intellectual cost, and it doesn't need to be that way. It *shouldn't* be that way.

> It's astonishing that s.tupleof is a sequence of ref's as you
> > say...
>
> BTW that was just how I understand `.tupleof`. I see it as (a superset of) a sequence of implicit ref declarations.
>

Okay, so you might be wrong? Whether you are or aren't is irrelevant
though, this is further evidence of the problem... it's not a "reasonable"
design in fundamental terms. It's actually quite literally anti-reasonable.
It's just internal compiler magic; the whole thing is a big special case,
which creates edge cases at almost every single point of contact.
If you need a __traits to know a fundamental primitive fact like ref,
something already went wrong.

> does anything else in the language have a semantic like that?
> > How could I synthesise something like that from anything else if I wanted to?
>
> If you mean synthesize `tupleof`, I already showed how (without introspection support), the first example here: https://forum.dlang.org/post/fctetiyhbiyhadlmcyzr@forum.dlang.org
>
> That example literally works with dmd recent git.
>

I'm very keen to play with ref locals when it lands; it's been such a long
time coming.
That's not quite the same thing though; you've declared 2 local variables;
does that infer they allocate stack space? Do I rely on the optimiser now
to remove them? It looks like yet-another-workaround/hack to me.


August 30
On Thursday, 29 August 2024 at 02:29:21 UTC, Manu wrote:
>> I've heard that C++ references are a complete swamp of special cases (regardless of lvalue vs rvalue reference).
>>
>
> You've "heard"? I'm sorry, but your qualification to comment on the topic is definitely problematic from here on...

Perhaps you'd like to see what someone who wrote a C++ compiler thinks:

> You might also consider the "uniformity" of the ref type in C++. It's awful - it's a special case EVERYWHERE in the C++ type system! It just does not fit as a type qualifier.

https://forum.dlang.org/post/m60oa4$kd5$1@digitalmars.com
August 30
On Thursday, 29 August 2024 at 02:29:21 UTC, Manu wrote:
>> I've heard that C++ references are a complete swamp of special cases (regardless of lvalue vs rvalue reference).
>>
>
> You've "heard"? I'm sorry, but your qualification to comment on the topic is definitely problematic from here on...

Herb Sutter:

> References are for parameter passing, including range-for. Sometimes they’re useful as local variables

https://herbsutter.com/2020/02/23/references-simply/

And in Herb's C++2 project, references are not part of the type. There is `inout`, which is a parameter storage class, like D's `ref`, and like all modern languages I can think of.
August 31
On 8/28/24 17:19, Nick Treleaven wrote:
>> It's astonishing that s.tupleof is a sequence of ref's as you say...
> 
> BTW that was just how I understand `.tupleof`. I see it as (a superset of) a sequence of implicit ref declarations.

Well, but that is not what it is.

```d
alias Seq(T...)=T;

struct S{
    int x;
    alias expand=Seq!x;
}

void main(){
    S s;
    pragma(msg,__traits(isRef,s.tupleof[0])); // false
    pragma(msg,__traits(isSame,s.expand,s.tupleof)); // true
}

```
August 31
On Saturday, 31 August 2024 at 06:45:00 UTC, Timon Gehr wrote:
> On 8/28/24 17:19, Nick Treleaven wrote:
>>> It's astonishing that s.tupleof is a sequence of ref's as you say...
>> 
>> BTW that was just how I understand `.tupleof`. I see it as (a superset of) a sequence of implicit ref declarations.
>
> Well, but that is not what it is.
>
> ```d
> alias Seq(T...)=T;
>
> struct S{
>     int x;
>     alias expand=Seq!x;
> }
>
> void main(){
>     S s;
>     pragma(msg,__traits(isRef,s.tupleof[0])); // false
>     pragma(msg,__traits(isSame,s.expand,s.tupleof)); // true
> }
>
> ```

OK, thanks. And `s.expand[0].writeln();` works too.

So the following error makes sense:

    alias x = s.expand[0];
    x.writeln(); // Error: accessing non-static variable `x` requires an instance of `S`
August 31
On Thursday, 29 August 2024 at 02:29:21 UTC, Manu wrote:
> Did you miss my point? The language can't *express *these things... we need backdoor information to hold the concept in any sort of expression.

Being able to compose attributes would be useful, yes. I'm not sure how important having that is though.

>> BTW that was just how I understand `.tupleof`. I see it as (a superset of) a sequence of implicit ref declarations.
>>
>
> Okay, so you might be wrong?

Yes, sorry. See Timon's reply to me.

>> If you mean synthesize `tupleof`, I already showed how (without introspection support), the first example here: https://forum.dlang.org/post/fctetiyhbiyhadlmcyzr@forum.dlang.org
>>
>> That example literally works with dmd recent git.
>>
>
> I'm very keen to play with ref locals when it lands; it's been such a long
> time coming.
> That's not quite the same thing though; you've declared 2 local variables;
> does that infer they allocate stack space? Do I rely on the optimiser now
> to remove them? It looks like yet-another-workaround/hack to me.

Well if .tupleof didn't exist and I didn't have write access to the source aggregate type (or I wanted it on a static array), then I could model it with a mixin that expands to my local ref declaration sequence alias. But yes that might well be less efficient.