DIP 1022--foreach auto ref--Community Review Round 1

Dukc ajieskola at gmail.com
Wed Aug 14 15:39:31 UTC 2019


On Tuesday, 13 August 2019 at 23:34:32 UTC, Manu wrote:
>
> struct S
> {
>   int front();
>   ...
> }
>
> void takesRef(ref int);
>
> {
>   S s;
>
>   takesRef(s.front()); // <- fine, temporary is created and 
> passed by ref
>
>   foreach(ref i; s) // <- error: no temporary is created; we 
> have
> decided to emit an error where an rval is given to a loop 
> counter
>   { ... }
> }
>
> This is the special case I'm talking about.

Nope, does not pass: `Error: function onlineapp.takesRef(ref int 
integer) is not callable using argument types (int)`


>> > Okay, here's some experiment cases:
>> >
>> >   int x = 1;
>> >   static foreach (i; AliasSeq!(10, x, 20))
>> >       pragma(msg, __traits(isRef, i));
>> >
>> >> false false false
>
> Iterating a tuple is a compile-time expansion, which is 
> distinct from
> a runtime expansion.
> The result is `false false false` either way, but static and
> non-static are very different operations.
>
> `foreach` will iterate an array of int's, `static foreach` will 
> expand the tuple and `i` will be an alias of each element.
>

No. `foreach` over an alias sequence will be unrolled at compile 
time, and behave like a `static foreach` does with CTFEd arrays. 
That was the way we did what `static foreach` does now, remember?

But I have to confess I'm not sure what `static foreach` does 
over an alias sequence. I ran a few tests, and some compiled 
despite my expectations, but didn't get a clear picture what it 
does. I think I need to read `static foreach` DIP again.

>> Same answer for the rest of examples with tuples.
>
> No, static and non-static foreach are completely different 
> semantically.
> My examples may show the same outputs either way, but it's easy 
> to
> make cases which show the distinction between static and 
> non-static.

See above.

>> Yes, these are the cases I meant with `static foreach`. I 
>> think `[10, 20, 30]` is a rvalue, and if the latter of these 
>> two examples compiles, it should not.
>
> Actually, because of D's 'weird shit' law, the array is not an 
> rvalue.
> (I think...?)

It is. I tested that: `Error: constant value 10 cannot be ref`

>
>> > As above, we could allow this by creating temporaries the 
>> > same as function arguments... but we've decided not to allow 
>> > ref iterators from rvalues.
>> >
>> >   static foreach (auto ref i; [10, 20, 30])
>> >       pragma(msg, __traits(isRef, i));
>> >
>> >> false false false ??
>>
>> Yes, exactly what's supposed to happen.
>
> Right... what's your point?
> Incidentally, I'm not sure this will hold; because array 
> literals are
> not rvalues, so I think it might be `true true true` here...

Because the loop won't work with `ref`, it falls back to 
iteration-by-copy. But you're right that if the literals were not 
rvalues, that would result in `true true true`.

>> > I guess the whole array is an rvalue, so then the loop 
>> > counter would take copies of the elements?
>> >
>> > It gets interesting when you do this:
>> >
>> >   int[3] x = [10, 20, 30];
>> >   static foreach (ref i; x)
>> >       pragma(msg, __traits(isRef, i));
>> >
>> >> true true true
>
> Why not? It's an alias... it should compile just fine.

x is a runtime static array. Tested that also: `Error: variable x 
cannot be read at compile time`

>
>> >
>> > And:
>> >
>> >   int[3] x = [10, 20, 30];
>> >   static foreach (auto ref i; x)
>> >       pragma(msg, __traits(isRef, i));
>
> `x` is not an enum, it's a local.
> The cases I propose to consider are exactly what I wrote, not 
> some other thing.

You are right it is not an enum, like I also just said. But 
that's exactly the reason why it cannot compile, `ref` (or `auto 
ref`) or no. `static foreach` requires the values of the loop 
aggregate to be available at compile time, not just it's length.

I might well support an argument that this shouldn't be the case, 
but as it is, my DIP should not need to discuss that case.

>
> For instance:
>
> int fun(ref int x)
> {
>   static foreach (i; AliasSeq!(10, x, 30))  // <- outputs 
> `false true false`
>     pragma(msg, __traits(isRef, i));
>
>   foreach (i; AliasSeq!(10, x, 30)) // <- outputs `false false 
> false` (!!!)
>     pragma(msg, __traits(isRef, i));
> }
>
> [snip]
> I actually think this form of runtime foreach should be 
> deprecated, if
> you want to do this with runtime foreach, wrap the tuple in `[ 
> ]`.

An interesting idea for another potential DIP.

>
>
> I'm not strictly talking about what does actually compile, I'm 
> talking about what makes semantic sense, and would be uniform 
> semantically with no bizarre cases. Some cases might not work, 
> but should.

Perhaps, in some cases. But again, it's outside the scope of this 
DIP.



More information about the Digitalmars-d mailing list