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