| Thread overview | |||||||
|---|---|---|---|---|---|---|---|
|
August 09, 2017 Cannot use std.array.Appender in recursive types | ||||
|---|---|---|---|---|
| ||||
Why doesn't appending to `subs` work with std.array.Appender in
struct T
{
string src;
import std.array : Appender;
Appender!(T[]) subs;
}
T t;
t.subs ~= T.init; // ERRORS
t.subs.put(T.init); // ERRORS
when it works with builtin arrays as in
struct S
{
string src;
S[] subs;
}
S s;
s.subs ~= S.init;
?
Specifically
t.subs ~= T.init
errors as
Error: cannot append type T to type Appender!(T[])
and
t.subs.put(T.init);
errors as
Error: template std.array.Appender!(T[]).Appender.put cannot deduce function from argument types !()(T), candidates are:
| ||||
August 09, 2017 Re: Cannot use std.array.Appender in recursive types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | On 8/9/17 2:25 PM, Nordlöw wrote:
> Why doesn't appending to `subs` work with std.array.Appender in
>
> struct T
> {
> string src;
> import std.array : Appender;
> Appender!(T[]) subs;
> }
> T t;
> t.subs ~= T.init; // ERRORS
> t.subs.put(T.init); // ERRORS
>
> when it works with builtin arrays as in
>
> struct S
> {
> string src;
> S[] subs;
> }
> S s;
> s.subs ~= S.init;
>
> ?
>
> Specifically
>
> t.subs ~= T.init
>
> errors as
>
> Error: cannot append type T to type Appender!(T[])
Here is the problem:
ElementType!(T[]) is void.
Here is ElementType:
template ElementType(R)
{
static if (is(typeof(R.init.front.init) T))
alias ElementType = T;
else
alias ElementType = void;
}
So what is happening here (I think), is that T isn't fully defined, so T.init.front.init is an error at this point. Therefore the else clause is selected.
This is one of the problems with using is(typeof) (or __traits(compiles)) with an "else" clause. You may not be checking what you think you are checking, and end up with the wrong result.
So essentially, Appender!(T[]) inside a T becomes (essentially) Appender!(void[]). And you can't append a T to a void[].
If I change the definition of ElementType to use R.init.front instead of R.init.front.init, it compiles. But I'm pretty sure this will break other ranges.
Somewhere, there's an answer.
-Steve
| |||
August 09, 2017 Re: Cannot use std.array.Appender in recursive types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 09.08.2017 21:00, Steven Schveighoffer wrote:
> On 8/9/17 2:25 PM, Nordlöw wrote:
>> Why doesn't appending to `subs` work with std.array.Appender in
>>
>> struct T
>> {
>> string src;
>> import std.array : Appender;
>> Appender!(T[]) subs;
>> }
>> T t;
>> t.subs ~= T.init; // ERRORS
>> t.subs.put(T.init); // ERRORS
>>
>> when it works with builtin arrays as in
>>
>> struct S
>> {
>> string src;
>> S[] subs;
>> }
>> S s;
>> s.subs ~= S.init;
>>
>> ?
>>
>> Specifically
>>
>> t.subs ~= T.init
>>
>> errors as
>>
>> Error: cannot append type T to type Appender!(T[])
>
> Here is the problem:
>
> ElementType!(T[]) is void.
>
> Here is ElementType:
> template ElementType(R)
> {
> static if (is(typeof(R.init.front.init) T))
> alias ElementType = T;
> else
> alias ElementType = void;
> }
>
> So what is happening here (I think), is that T isn't fully defined, so T.init.front.init is an error at this point. Therefore the else clause is selected.
>
> This is one of the problems with using is(typeof) (or __traits(compiles)) with an "else" clause. You may not be checking what you think you are checking, and end up with the wrong result.
>
> So essentially, Appender!(T[]) inside a T becomes (essentially) Appender!(void[]). And you can't append a T to a void[].
>
> If I change the definition of ElementType to use R.init.front instead of R.init.front.init, it compiles. But I'm pretty sure this will break other ranges.
>
> Somewhere, there's an answer.
>
> -Steve
It's a forward reference bug. Self-contained test case:
auto front(T)(T[] a){ return a[0]; }
template ElementType(R){
pragma(msg, typeof(R.init.front)); /* Error: struct bug.T no size because of forward reference */
static if (is(typeof(R.init.front) T)) alias ElementType = T;
else alias ElementType = void;
}
struct Appender(A){
A a;
alias T=ElementType!A;
private enum canPutItem(U)=is(U==T);
void put(T)(T t)if(canPutItem!T){
a~=t;
}
}
struct T{
string src;
Appender!(T[]) subs;
}
void main(){
T t;
t.subs.put(T.init);
}
| |||
August 09, 2017 Re: Cannot use std.array.Appender in recursive types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Wednesday, 9 August 2017 at 19:00:54 UTC, Steven Schveighoffer wrote:
> If I change the definition of ElementType to use R.init.front instead of R.init.front.init, it compiles. But I'm pretty sure this will break other ranges.
If Phobos compiles with the change would that change deserve a PR?
| |||
August 09, 2017 Re: Cannot use std.array.Appender in recursive types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Nordlöw | On 8/9/17 6:24 PM, Nordlöw wrote:
> On Wednesday, 9 August 2017 at 19:00:54 UTC, Steven Schveighoffer wrote:
>> If I change the definition of ElementType to use R.init.front instead of R.init.front.init, it compiles. But I'm pretty sure this will break other ranges.
>
> If Phobos compiles with the change would that change deserve a PR?
I'm pretty sure it will fail in cases where front is a non-@property function.
I think in terms of Timon's post, he is doing what I did, and it's not working. I think it's a forward reference bug, and that just needs to be fixed.
-Steve
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply