Thread overview
Help me investigate a bug to file it.
Jul 08, 2013
monarch_dodra
Jul 08, 2013
Artur Skawina
Jul 08, 2013
monarch_dodra
Jul 08, 2013
Artur Skawina
Jul 09, 2013
Artur Skawina
Jul 10, 2013
monarch_dodra
Jul 10, 2013
Artur Skawina
July 08, 2013
Yeah, not very exiting, but I just spent an hour tracking down the fix, but now I'd like to track down *what* was making it break (my fix was "luck"). This should help Kenji (or others) have an easier time fixing it :)

Also, I'm only repro'ing this on linux...

I think there are two things involved here. The first, is accessing members of a struct for a static if, in a global scope:

//----
import std.stdio;
import std.range;

struct S(R)
{
    R _input;
    void foo()
    {
        static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
        static assert(is(typeof(_input[size_t.max .. $]))); //ok
    }
    static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
    static assert(is(typeof(_input[size_t.max .. $]))); //NOPE!
}

void main()
{
    auto k = iota(0, 1).cycle();
    S!(typeof(k)) s;
}
//----

As you can see, the static if behaves differently in a function, or in the raw body of the struct. Is this normal? Anybody know what is going on exactly? I'm trying to get more context here...

-----------------------------------------------

Second, one of the things I found strange was that only *1* of the two assertions failed. basically, _input[0 .. 1] is fair game, yet, _input[0 .. $] is not? What gives? I put my nose in cycles's opDollar. It is written like this:

//----
        private static struct DollarToken {}
        DollarToken opDollar()
        {
            return DollarToken.init;
        }
//----
Nothing special about it. Just a function that returns an object. Declaring it const/nothrow/@safe *and* pure does not fix the problem...

*HOWEVER* declaring it as a manifest constant *does* fix the issue:
//----
        private static struct DollarToken {}
        enum opDollar = DollarToken.init;
//----

------------------------------------------------

I think I've run into "global static ifs using members" issues before, but just figured I wasn't allowed to use them. Apparently, to issue is more subtle than this. I have *no* idea how to file this, I have no idea what the rules are. Any insight?
July 08, 2013
On 07/08/13 19:54, monarch_dodra wrote:
> struct S(R)
> {
>     R _input;
>     void foo()
>     {
>         static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>         static assert(is(typeof(_input[size_t.max .. $]))); //ok
>     }
>     static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>     static assert(is(typeof(_input[size_t.max .. $]))); //NOPE!
> }

> As you can see, the static if behaves differently in a function, or in the raw body of the struct. Is this normal? Anybody know what is going on exactly? I'm trying to get more context here...
> 
> -----------------------------------------------
> 
> Second, one of the things I found strange was that only *1* of the two assertions failed. basically, _input[0 .. 1] is fair game, yet, _input[0 .. $] is not? What gives? I put my nose in cycles's opDollar. It is written like this:

Did you really mean size_t.*max* above?

Anyway, when '$' is rewritten as 'opDollar' and that is defined as a non-static method, it would be surprising if calling the method did work w/o any context pointer...

> //----
>         private static struct DollarToken {}
>         DollarToken opDollar()
>         {
>             return DollarToken.init;
>         }
> //----
> Nothing special about it. Just a function that returns an object. Declaring it const/nothrow/@safe *and* pure does not fix the problem...
> 
> *HOWEVER* declaring it as a manifest constant *does* fix the issue:
> //----
>         private static struct DollarToken {}
>         enum opDollar = DollarToken.init;
> //----

Does declaring the opDollar "function" as `static` fix it?

artur
July 08, 2013
On Monday, 8 July 2013 at 18:55:30 UTC, Artur Skawina wrote:
> On 07/08/13 19:54, monarch_dodra wrote:
>> struct S(R)
>> {
>>     R _input;
>>     void foo()
>>     {
>>         static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>>         static assert(is(typeof(_input[size_t.max .. $]))); //ok
>>     }
>>     static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>>     static assert(is(typeof(_input[size_t.max .. $]))); //NOPE!
>> }
>
>> As you can see, the static if behaves differently in a function, or in the raw body of the struct. Is this normal? Anybody know what is going on exactly? I'm trying to get more context here...
>> 
>> -----------------------------------------------
>> 
>> Second, one of the things I found strange was that only *1* of the two assertions failed. basically, _input[0 .. 1] is fair game, yet, _input[0 .. $] is not? What gives? I put my nose in cycles's opDollar. It is written like this:
>
> Did you really mean size_t.*max* above?

Yeah. It's not a runtime test. In this case, I *also* need to make sure that the slie operation accepts a size_t arg. If I use size_t.min, it statically evaluates to zero, so the test becomes sens-less: saying "fun(size_t.min)" works doesn't actually guarantee you can pass a size_t: it may actually only accepts ubytes or short indexing.

But I guess that is a detail in the scope of the original problem.

> Anyway, when '$' is rewritten as 'opDollar' and that is defined
> as a non-static method, it would be surprising if calling the
> method did work w/o any context pointer...

But, the "context pointer" *should* be defined as whatever the owner of the indexing/slicing object is, no? In this case, it's simply _input. It has nothing to do with the "this" pointer being available...

If anything, it kind of worries me about *what* the implementation is doing with the this pointer, but I haven't been able to "trick" it into calling a wrong function.

> Does declaring the opDollar "function" as `static` fix it?
>
> artur

Yes. It does fix it. I think you put your finger exactly on where the issue is. I'll report it in the morning. Thank you for participating :)
July 08, 2013
On 07/08/13 23:45, monarch_dodra wrote:
> On Monday, 8 July 2013 at 18:55:30 UTC, Artur Skawina wrote:
>> On 07/08/13 19:54, monarch_dodra wrote:
>>> struct S(R)
>>> {
>>>     R _input;
>>>     void foo()
>>>     {
>>>         static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>>>         static assert(is(typeof(_input[size_t.max .. $]))); //ok
>>>     }
>>>     static assert(is(typeof(_input[size_t.max .. size_t.max]))); //ok
>>>     static assert(is(typeof(_input[size_t.max .. $]))); //NOPE!
>>> }
>>
>>> As you can see, the static if behaves differently in a function, or in the raw body of the struct. Is this normal? Anybody know what is going on exactly? I'm trying to get more context here...
>>>
>>> -----------------------------------------------
>>>
>>> Second, one of the things I found strange was that only *1* of the two assertions failed. basically, _input[0 .. 1] is fair game, yet, _input[0 .. $] is not? What gives? I put my nose in cycles's opDollar. It is written like this:
>>
>> Did you really mean size_t.*max* above?
> 
> Yeah. It's not a runtime test. In this case, I *also* need to make sure that the slie operation accepts a size_t arg. If I use size_t.min, it statically evaluates to zero, so the test becomes sens-less: saying "fun(size_t.min)" works doesn't actually guarantee you can pass a size_t: it may actually only accepts ubytes or short indexing.

Didn't think of that; these implicit narrowing conversion can be, umm, surprising. But as long as the right overload gets picked, I guess they're safe. Thanks for the explanation.


>> Anyway, when '$' is rewritten as 'opDollar' and that is defined as a non-static method, it would be surprising if calling the method did work w/o any context pointer...
> 
> But, the "context pointer" *should* be defined as whatever the owner of the indexing/slicing object is, no? In this case, it's simply _input. It has nothing to do with the "this" pointer being available...
> 
> If anything, it kind of worries me about *what* the implementation is doing with the this pointer, but I haven't been able to "trick" it into calling a wrong function.

It's just like

   struct S {
      auto opDollar() { return 42; }
      enum a = opDollar();
   }

This can't work - there is no S object to call the method with.

      enum a = S.init.opDollar();

would work, just as making the method static.


>> Does declaring the opDollar "function" as `static` fix it?
> 
> Yes. It does fix it. I think you put your finger exactly on where the issue is. I'll report it in the morning. Thank you for participating :)

Thank you for still trying to improve the std lib.
I gave up on using it, other than for toy examples, long time ago.
Hopefully it will be usable at some point.

artur
July 09, 2013
On 07/09/13 00:43, Artur Skawina wrote:
> On 07/08/13 23:45, monarch_dodra wrote:
>> But, the "context pointer" *should* be defined as whatever the owner of the indexing/slicing object is, no? In this case, it's simply _input. It has nothing to do with the "this" pointer being available...
>>
>> If anything, it kind of worries me about *what* the implementation is doing with the this pointer, but I haven't been able to "trick" it into calling a wrong function.
> 
> It's just like

Sorry, didn't read that code again before replying. What is
indeed unintuitive is that the first typeof expression succeeds
but the other one fails. But this is related to how typeof() works,
apparently by design. Eg:

   auto f(int a) { return a; }
   pragma(msg, typeof(f(1))); // "int". OK.
   alias INT = int;
   pragma(msg, typeof(f(INT))); // "int". OK...
   //pragma(msg, typeof(f(int))); // Compile error.

That plus a non-existing this._input, results in a bit (too much)
magic, and the first test passes. The other one fails because of
the '$' (ie opDollar() call).

artur
July 10, 2013
On Tuesday, 9 July 2013 at 00:06:11 UTC, Artur Skawina wrote:
> That plus a non-existing this._input, results in a bit (too much)
> magic, and the first test passes. The other one fails because of
> the '$' (ie opDollar() call).
>
> artur

In any case, it is now filed:
http://d.puremagic.com/issues/show_bug.cgi?id=10597

I reduced it to this (imo) neat usecase:

//----
struct R
{
    void opIndex(int);
    int opDollar();
}
R r;

void foo()
{
    static assert(is(typeof(r[0]))); //ok
    static assert(is(typeof(r[$]))); //ok
}

static assert(is(typeof(r[0]))); //ok
static assert(is(typeof(r[$]))); //fails (!)
//----

Thanks again for the help :)
July 10, 2013
On 07/10/13 21:36, monarch_dodra wrote:
> On Tuesday, 9 July 2013 at 00:06:11 UTC, Artur Skawina wrote:
>> That plus a non-existing this._input, results in a bit (too much)
>> magic, and the first test passes. The other one fails because of
>> the '$' (ie opDollar() call).
> In any case, it is now filed: http://d.puremagic.com/issues/show_bug.cgi?id=10597
> 
> I reduced it to this (imo) neat usecase:

> struct R
> {
>     void opIndex(int);
>     int opDollar();
> }
> R r;

> static assert(is(typeof(r[0]))); //ok
> static assert(is(typeof(r[$]))); //fails (!)

Yeah, I can't convince myself that it's ok for the /first/ assert to pass - actually evaluating the "r[0]" expression in that same context will *fail* (because 'r' isn't known at compile time). It always possible to test with 'typeof(r.init[0])'.

The fact that

   static assert(__traits(compiles, r[0]));

succeeds, when that expression won't actually compile is also a problem.

artur