alias x = s.tupleof; // this works
alias y = s.tupleof[0]; // this is a compile error?!
It's long past time to fix this.
alias has an overwhelming sea of edge cases. It's extremely off-putting to potential D users, because this category of meta is absolutely integral to why they try out the language at all.
Comment #1 by turkeyman — 2024-08-24T17:49:07Z
Also this:
alias z = AliasSeq!(s.tupleof); // error, but it should be a no-op
Comment #2 by nick — 2024-08-28T11:24:11Z
> alias y = s.tupleof[0]; // this is a compile error?!
Inside a function, `.tupleof` on a struct/static array instance should be implemented as a symbol sequence of implicit ref declarations:
struct S
{
int i;
char c;
}
void main()
{
S s = {2, 'c'};
ref __si = s.i;
ref __sc = s.c;
alias tupleof = AliasSeq!(__si, __sc); // should be the same as s.tupleof
alias a = tupleof[0]; // works
a++;
assert(s.i == 3);
alias z = AliasSeq!tupleof; // works
assert(++z[0] == 4);
}
> alias z = AliasSeq!(s.tupleof); // error, but it should be a no-op
That line doesn't error, but using z does:
z[0].writeln(); // Error: accessing non-static variable `x` requires an instance of `S`
Comment #3 by nick — 2024-08-28T14:54:01Z
> should be implemented as a symbol sequence of implicit ref declarations
Actually `AliasSeq!(__si, __sc)` can't be the lowering because then `__traits(identifier, s.tupleof[0])` would be `__si`, not `i`. But conceptually it should be a superset of an lvalue sequence.
Comment #4 by nick — 2024-08-31T09:41:20Z
> alias y = s.tupleof[0]; // this is a compile error?!
The error is:
Error: alias `y` cannot alias an expression `AliasSeq!(s.x)[0]`
That is because an alias declaration cannot target a value.
For:
struct S {
int x;
alias expand=Seq!x;
}
Turns out .tupleof is actually equivalent to the `expand` alias, see:
https://forum.dlang.org/post/[email protected]
alias z = AliasSeq!(s.tupleof);
z[0].writeln(); // Error: accessing non-static variable `x` requires an instance of `S`
So the above error is consistent with the design of symbol aliases. Changing this issue to an enhancement.
Comment #5 by nick — 2024-08-31T09:46:21Z
> That is because an alias declaration cannot target a value.
Sorry, s.x can be aliased as a symbol.
alias y = s.tupleof[0]; // error
alias x = s.x; // OK
x.writeln; // error, no instance of S - correct by design
So there is a bug, the `y` alias should work like `x`.
Comment #6 by turkeyman — 2024-08-31T22:57:25Z
I just want to be clear again; it's definitely not "correct by design", it is absolutely, and proven time and time again over decades INCORRECT by design, and this just needs to be fixed.
The expression is expected to work by everyone who approaches it, and it should work.
Comment #7 by maxsamukha — 2024-09-01T09:23:39Z
(In reply to Manu from comment #6)
> decades INCORRECT by design
Not incorrect, but buggy and definitely not what most people expect.
Comment #8 by turkeyman — 2024-09-01T11:49:18Z
Nar, 'incorrect' is the only possible description, and I will die on this hill.
"Most people" is an understatement ;)
'Explaining' things at the expense of reality is a physiological trap that our kind fall into very easily.
This particular thing has gone on for way way too long, and it's time we put it to bed.
Comment #9 by turkeyman — 2024-09-01T11:50:40Z
**psychological trap... bloody autocorrect!
Comment #10 by maxsamukha — 2024-09-01T13:08:13Z
(In reply to Manu from comment #8)
> Nar, 'incorrect' is the only possible description, and I will die on this
> hill.
> "Most people" is an understatement ;)
>
> 'Explaining' things at the expense of reality is a physiological trap that
> our kind fall into very easily.
> This particular thing has gone on for way way too long, and it's time we put
> it to bed.
Anyway, if you guys are going to "fix" that, please make sure that there is still a way to get a "contextless" reference to a member, that is, (an alternative of) this still works:
void apply(alias foo)(ref S s)
{
assert (__traits(child, s, foo) == 1);
int delegate() dg;
dg.ptr = &s;
dg.funcptr = &foo;
assert(dg() == 1);
}
struct S
{
int x;
int foo() => x;
}
void main()
{
S s = S(1);
apply!(S.foo)(s);
}
Comment #11 by turkeyman — 2024-09-01T13:12:51Z
Yeah it's fine, just alias a member of the type rather than the instance.
You can typeof(instance) of you want to use instance as the point of reference.
Comment #12 by maxsamukha — 2024-09-01T13:57:30Z
(In reply to Manu from comment #11)
> Yeah it's fine, just alias a member of the type rather than the instance.
> You can typeof(instance) of you want to use instance as the point of
> reference.
There are also cases where the explicitly specified static context is ignored, as in:
struct S
{
void foo()
{
pragma(msg, typeof(&S.foo)); // void delegate();
auto f = &S.foo;
pragma(msg, typeof(f)); // void delegate();
}
pragma(msg, typeof(&S.foo)); // void function();
}
It'd be nice to see them fixed too.
Comment #13 by turkeyman — 2024-09-01T23:12:55Z
Ah yes, good point. I also had an encounter with that exact surprise about 2 days ago aswell.
I couldn't work out how to explain that... I just had to shake me head and move on.
Comment #14 by maxsamukha — 2024-09-02T05:32:12Z
(In reply to Manu from comment #13)
> Ah yes, good point. I also had an encounter with that exact surprise about 2
> days ago aswell.
> I couldn't work out how to explain that... I just had to shake me head and
> move on.
The same logic as that of alias - the reference is resolved to a contextless symbol, and then the implicit 'this' is used as the context.
struct S
{
int x;
void foo()
{
alias y = S.x;
auto z = y; // `z` is resolved to `this.x`;
}
}
(Disclaimer: I'm not defending the "symbol" nonsense).
Comment #15 by maxsamukha — 2024-09-02T05:48:41Z
(In reply to Max Samukha from comment #14)
> auto z = y; // `z` is resolved to `this.x`;
`y`, not `z`;
Comment #16 by robert.schadek — 2024-12-13T19:36:57Z