Issue 24717 - alias edge cases with tupleof
Summary: alias edge cases with tupleof
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P3 normal
Assignee: No Owner
URL: http://dlang.org/library/std/typecons...
Keywords:
Depends on:
Blocks:
 
Reported: 2024-08-24 17:47 UTC by Manu
Modified: 2024-09-02 05:48 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Manu 2024-08-24 17:47:46 UTC
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 Manu 2024-08-24 17:49:07 UTC
Also this:

alias z = AliasSeq!(s.tupleof); // error, but it should be a no-op
Comment 2 Nick Treleaven 2024-08-28 11:24:11 UTC
> 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 Nick Treleaven 2024-08-28 14:54:01 UTC
> 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 Nick Treleaven 2024-08-31 09:41:20 UTC
> 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/vaue5d$1quo$1@digitalmars.com

    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 Nick Treleaven 2024-08-31 09:46:21 UTC
> 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 Manu 2024-08-31 22:57:25 UTC
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 Max Samukha 2024-09-01 09:23:39 UTC
(In reply to Manu from comment #6)
> decades INCORRECT by design

Not incorrect, but buggy and definitely not what most people expect.
Comment 8 Manu 2024-09-01 11:49:18 UTC
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 Manu 2024-09-01 11:50:40 UTC
**psychological trap... bloody autocorrect!
Comment 10 Max Samukha 2024-09-01 13:08:13 UTC
(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 Manu 2024-09-01 13:12:51 UTC
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 Max Samukha 2024-09-01 13:57:30 UTC
(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 Manu 2024-09-01 23:12:55 UTC
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 Max Samukha 2024-09-02 05:32:12 UTC
(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 Max Samukha 2024-09-02 05:48:41 UTC
(In reply to Max Samukha from comment #14)

>         auto z = y; // `z` is resolved to `this.x`;

`y`, not `z`;