Thread overview
A proper WAT moment
Oct 14, 2019
John Colvin
Oct 14, 2019
Paul Backus
Oct 15, 2019
John Colvin
Oct 15, 2019
Simen Kjærås
Oct 15, 2019
Paul Backus
Oct 16, 2019
Jacob Carlborg
October 14, 2019
Different ability to access a property depending if I'm inside something else when I look?

struct S
{
    int a;
    static int b;
    int c() { return a; }
    static int d() { return 3; }
    int e() @property { return a; }
    static int f() @property { return 3; }
}

void foo(S s)
{
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "a")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "a")));

    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "b")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "b")));

    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "c")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "c")));

    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "d")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "d")));

    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "e")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "e")));

    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "f")));
    pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "f")));
}

struct C(S)
{
    void foo(S s)
    {
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "a")));
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "a")));

        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "b")));
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "b")));

        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "c")));
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "c")));

        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "d")));
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "d")));

        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "e")));
        // ALL True except for this one:
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "e")));

        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, s, "f")));
        pragma(msg, __LINE__, " ", __traits(compiles, __traits(getMember, S, "f")));
    }
}

alias C0 = C!S;
October 14, 2019
On Monday, 14 October 2019 at 17:00:56 UTC, John Colvin wrote:
> Different ability to access a property depending if I'm inside something else when I look?
>
> [snip]

You're attempting to call one of S's member functions without an instance of S to call it on. Reduced version:

struct S
{
    int a;
    int e() @property { return a; }
}

void foo(S s)
{
    pragma(msg, __LINE__, " ", __traits(compiles, S.e)); // true (???)
    S.e; // Error: need `this` for `e` of type `@property int()`
}

struct C
{
    void foo(S s)
    {
        pragma(msg, __LINE__, " ", __traits(compiles, S.e)); // false
        S.e; // Error: `this` for `e` needs to be type `S` not type `C`
    }
}

The real issue here is that the first `__traits(compiles)` check succeeds, even though the actual expression fails.
October 15, 2019
On Monday, 14 October 2019 at 19:45:11 UTC, Paul Backus wrote:
> On Monday, 14 October 2019 at 17:00:56 UTC, John Colvin wrote:
>> Different ability to access a property depending if I'm inside something else when I look?
>>
>> [snip]
>
> You're attempting to call one of S's member functions without an instance of S to call it on. Reduced version:
>
> struct S
> {
>     int a;
>     int e() @property { return a; }
> }
>
> void foo(S s)
> {
>     pragma(msg, __LINE__, " ", __traits(compiles, S.e)); // true (???)
>     S.e; // Error: need `this` for `e` of type `@property int()`
> }
>
> struct C
> {
>     void foo(S s)
>     {
>         pragma(msg, __LINE__, " ", __traits(compiles, S.e)); // false
>         S.e; // Error: `this` for `e` needs to be type `S` not type `C`
>     }
> }
>
> The real issue here is that the first `__traits(compiles)` check succeeds, even though the actual expression fails.

And all the other ones in my example that access members without an instance that also compile?

There's something pretty strange about the rules here.
October 15, 2019
On Tuesday, 15 October 2019 at 07:06:35 UTC, John Colvin wrote:
> On Monday, 14 October 2019 at 19:45:11 UTC, Paul Backus wrote:
>> On Monday, 14 October 2019 at 17:00:56 UTC, John Colvin wrote:
>>> Different ability to access a property depending if I'm inside something else when I look?
>>>
>>> [snip]
>>
>> You're attempting to call one of S's member functions without an instance of S to call it on.
[snip]
>> The real issue here is that the first `__traits(compiles)` check succeeds, even though the actual expression fails.
>
> And all the other ones in my example that access members without an instance that also compile?
>
> There's something pretty strange about the rules here.

Yeah, Paul's wrong here - the struct is what messes things up here, though I don't understand why. Just putting the first function inside a struct cause the exact same issue:

struct S {
    int a;
    int e() @property { return a; }
}

pragma(msg, __LINE__, " ", __traits(compiles,__traits(getMember, S, "e")));

void fun() {
    pragma(msg, __LINE__, " ", __traits(compiles,__traits(getMember, S, "e")));
}

struct S2 {
    void fun() {
        pragma(msg, __LINE__, " ", __traits(compiles,__traits(getMember, S, "e")));
    }
}

Interestingly, the code does of course actually compile:

struct S3 {
    void fun() {
        alias a = __traits(getMember, S, "e");
    }
}
October 15, 2019
On Tuesday, 15 October 2019 at 09:34:41 UTC, Simen Kjærås wrote:
> void fun() {
>     pragma(msg, __LINE__, " ", __traits(compiles,__traits(getMember, S, "e")));
> }

__traits(compiles) is lying to you again. If you replace it with

__traits(getMember, S, "e")

...you'll get an error.


> Interestingly, the code does of course actually compile:
>
> struct S3 {
>     void fun() {
>         alias a = __traits(getMember, S, "e");
>     }
> }

I think this is the key. It compiles if you put it on the right-hand side of an alias declaration, but *not* if you write it as a bare expression:

struct S3 {
    void fun() {
        __traits(getMember, S, "e"); // Error
    }
}

The difference is that in an alias declaration, __traits(getMember) isn't evaluated as a function call, whereas in a naked expression, it is. So the question becomes, why is __traits(getMember) evaluating @property functions differently in different contexts?

I suspect the answer has to do with the difference between the two error messages. In the context of a free function, it's "need `this` for `e`"--i.e., you're calling a member function without a receiver--so the compiler can infer that you didn't mean to call the function at all. In the context of a member function, however, it's "`this` needs to be type `S`, not `S2`"--i.e., you're (implicitly) calling a member function on the wrong type of receiver--so the compiler treats it the same as any other function call with incorrect arguments, and assumes you actually meant it.

Of course, this is speculation; it would take looking at the compiler source to be sure.
October 16, 2019
On 2019-10-15 09:06, John Colvin wrote:

> And all the other ones in my example that access members without an instance that also compile?
> 
> There's something pretty strange about the rules here.

The thing is that it should be possible to access a non-static member without an instance because it's possible to manually construct a delegate:

class S
{
    int a;
    int e() @property { return a; }
}

void foo()
{
    int function() f = &S.e; // this compiles
    int delegate() dg;
    S s;
    dg.ptr = &s;
    dg.funcptr = f;
}

struct C
{
    void bar()
    {
        int function() f = &S.e; // this fails for some reason but should compile
    }
}

So the expression `S.e` should compile, because it can be part of a large expression, i.e. `&S.e`, which should compile.

The strange thing is that it fails to compile inside `C`. But if `bar` is changed to a static method it compiles again.

-- 
/Jacob Carlborg