December 10, 2011
On 12/10/2011 11:43 AM, Mehrdad wrote:
> Oh, nice, here's an ICE for y'all :)
>
>
> struct Matrix(T)
> {
>     @property T[] data() { return null; }
>
>     int opApply(scope int delegate(ref size_t[], ref T) dg) { return 0; }
>     int opApply(scope int delegate(ref const(size_t[]), ref const(T)) dg) const { return 0; }
>
>     Matrix!(typeof(mixin("data[0] " ~ op ~ " data[0]"))) opBinary(string op)(Matrix other)
>     {
>         auto result = typeof(return)();
>         foreach (i, ref val; this)
>         { mixin("result[i] = val " ~ op ~ " other[i];"); }
>         return result;
>     }
> }
>
> void main() { auto m = Matrix!size_t(); m = m * m; }

BTW this _DOES_ look like another issue related to const.

Oh and the error is
annoy.d(12): Error: cannot uniquely infer foreach argument types
Assertion failure: '0' on line 144 in file 'statement.c'
December 10, 2011
On 12/10/2011 3:33 AM, Walter Bright wrote:
> On 12/10/2011 3:14 AM, Mehrdad wrote:
>> ... and another... (yes, this one _IS_ a const issue)
>>
>> struct S { int opApply(scope int delegate(ref inout(int)) dg) inout { return 0; } }
>> void main()
>> {
>> foreach (i; S()) { }
>> }
>>
>> Error: inout on parameter means inout must be on return type as well (if from D1
>> code, replace with 'ref')
>
> Right. inout has no point if it also does not appear on the return type.
>
> The purpose of inout is to transfer the 'constness' of the argument to the return type. If inout isn't on the return type somewhere, there's likely a design mistake in your code. It's like having:
>
> {
>     a + b;
> }
>
> Which is an error in D because such is pointless.

Oh yeah, I just realized -- I think you missed the _second_ 'inout'.
It's indeed on a parameter! It's just on the implicit 'this' parameter, not on an explicit one. This is a bug (IMO, arising from the fact that inout() is treated like a type constructor, whereas it shouldn't be).
December 10, 2011
http://d.puremagic.com/issues/show_bug.cgi?id=7091
December 10, 2011
On 12/10/2011 11:52 AM, Trass3r wrote:
> http://d.puremagic.com/issues/show_bug.cgi?id=7091
Thanks. Sorry I'm too lazy to do that myself lol -- kinda busy studying for exams and whatnot. (I really shouldn't be even working on this right now. :P)

Also, I just wanted to note:

I've found a bunch of these bugs as a result of trying to *work around* the previous bugs (!!!). For example, this one was a result of trying to fix the opApply-with-inout issue, by copy/pasting the code for the const and non-const versions.
But I guess it turned out mixins didn't play well with that?

_This_ fact (where working around one bug causing another) is why D's const looks broken to me. Hope it makes sense why I've been complaining about it so much!
December 10, 2011
>> http://d.puremagic.com/issues/show_bug.cgi?id=7091
> Thanks. Sorry I'm too lazy to do that myself lol -- kinda busy studying for exams and whatnot. (I really shouldn't be even working on this right now. :P)

Then I think posting it directly to bugzilla instead would be better.
Things get easily lost in the newsgroups.
December 10, 2011
On 12/10/2011 12:01 PM, Trass3r wrote:
>>> http://d.puremagic.com/issues/show_bug.cgi?id=7091
>> Thanks. Sorry I'm too lazy to do that myself lol -- kinda busy studying for exams and whatnot. (I really shouldn't be even working on this right now. :P)
>
> Then I think posting it directly to bugzilla instead would be better.
> Things get easily lost in the newsgroups.

See, but, it's a tradeoff: then I wouldn't be able to complain about it :'(
December 10, 2011
On Saturday, December 10, 2011 13:12:06 Andrei Alexandrescu wrote:
> On 12/10/11 5:27 AM, Walter Bright wrote:
> > On 12/10/2011 3:17 AM, Mehrdad wrote:
> >> Yup, I started using it as soon as Timon mentioned it (thanks for the
> >> suggestion!).
> >> But I was referring to the bug, not to the workaround. :)
> > 
> > It isn't a bug, it is designed to work that way.
> 
> The design is wrong.
> 
> > The type of the
> > instantiation is based on the argument types, not the parameter types.
> 
> This is wrong. The type of parameter is size_t, no two ways about it. It is NOT alias.
> 
> > (Note that "2" is the argument and "N" is the parameter.)
> 
> Not that N has type size_t.
> 
> > Hence the message:
> > 
> > Error: cannot implicitly convert expression (m) of type Matrix!(int,2)
> > to Matrix!(int,N)
> 
> The message reflects a mistake.
> 
> >> (I don't understand
> >> why Matrix!(int, cast(int)1) is considered a different instantiation,
> >> when it
> >> can't even be instantiated...)
> > 
> > Yes, it can be instantiated. But it cannot be implicitly converted to
> > type Matrix!(int, cast(uint)1) because they are different types.
> 
> They shouldn't.

Templates should _always_ be instantiated on the _value_ of their arguments. In the case such as

template t(T) {}
t!int

the template argument is a type, so the template is instantiated on that type. In the case of

template t(int i) {}
t!5

the argument is the value 5, so the template should be instatiated on the value 5, regardless of whether the argument is an int or uint or any other type implicitly convertible to the template's parameter's type.

I do not understand how Walter could think otherwise. Having

template t(int i) {}
t!5
t!5u

create two separate templates makes no sense. I don't see what value it could possible have. And with the addition of CTFE functions or other templates whose result is auto, it could cause real problems.

Bug for this: http://d.puremagic.com/issues/show_bug.cgi?id=3467

- Jonathan M Davis
December 10, 2011
On 12/10/2011 11:03 AM, Mehrdad wrote:
> So how are you supposed to implement opApply on a container (or e.g. here, a
> matrix)? Copy/paste the code for const- and non-const versions?

Internal to a function, inout behaves like 'const'. You won't be able to modify the data. Therefore, if there is no inout in the return type, use 'const' in the parameter list instead.

The purpose of inout is to transmit the 'constness' of the function argument type to the return type, using only one implementation of that function. That requires the function to internally regard inout as const.
December 10, 2011
On Saturday, December 10, 2011 05:45:11 bearophile wrote:
> Timon Gehr:
> > Just slice the const array to get a range. The specialization for ranges does not have the bug.
> > 
> > import std.algorithm;
> > void main() {
> > 
> >      const arr = [1, 2, 3];
> >      reduce!"a*b"(arr[]);   // It works.
> > 
> > }
> 
> Wasn't arr a range already?

Per isInputRange!(typeof(arr)), no. It has the right functions, but it can't use them, because it's const. A const range is essentially useless, because you can't iterate over it.

When a template is instantiated, it's instantiated on the exact types that it's given. So, if you given a const or immutable array, it's going to instantiate on that type, even though it _could_ theoretically instantiate itself with a mutable array with const or immutable elements. And since, a const or immutable array won't work as a range, the template instantiation fails.

The range-based functions in std.array and std.string work with const and immutable arrays simply because they are either coded specifically for arrays or have specializations for them. You need a function which takes const(T)[] rather than one which takes const T[]. std.array and std.string typically take const(T)[] rather than const T[], whereas a more general range function is taking R, which in the case above is determined to be const int[], which won't work as a range.

It used to be that the slice of an array was the exact type of that array, meaning Timon's solution wouldn't work without a cast, because the slice would be just as const as the original. Fortunately, that has been changed, so now a slice of a const or immutable array will have its elements be const or immutable but not the slice itself. So, now const and immutable arrays are like static arrays in that you need to slice them to use them with range-based functions, but you can't use them directly with range-based functions.

A bigger problem is const or immutable ranges which are structs. Unless the programmer who defined the range type managed to have an opSlice which returned a version with const or immutable elements but where the range itself wasn't const or immutable (i.e. a tail-const slice), then you can't even get slicing to work. And even if templates were improved to the point that they would instantiate const int[] as const(int)[] (which I expect is a change which will never happen due to the difficulties in doing so), that wouldn't solve the problem for ranges which aren't arrays.

Also, const and immutable ranges which aren't arrays which have no opSlice will _never_ work, because there's no way to get a mutable slice of them to operate on, so you're stuck with a const or immutable range.

Really what this means is that you need to slice const and immutable ranges when you pass them to range-based functions, and then as long as they're arrays or their opSlices have been defined properly, it'll work just fine.

If ranges had been designed more like slists (i.e. they have head and tail rather than front and popFront), then they would have been inherently tail- const and we wouldn't be having these problems. But that's less efficent, because you have to copy the range every time that you want to remove an element from it. Regardless, it's too late now. Ranges are too heavily used with their current API for us to change it that drastically now.

- Jonathan M Davis
December 10, 2011
On 12/10/2011 1:37 AM, Mehrdad wrote:
> (Sorry for being so critical but at least I tried to make it constructive...)

Actually, I think your post is very constructive and worthwhile. Thanks for taking the time to be specific about what issues you're running in to.