I discovered this inside phobos. First reduced test case:
//----
import std.stdio;
import std.range;
import std.conv;
void main()
{
auto a = iota(1, 2);
auto b = a[];
alias typeof(a) A;
alias typeof(b) B;
writeln("Typeof A: ", A.stringof);
writeln("Typeof B: ", B.stringof);
assert(is(A == B), text(A.stringof, " is different from " , B.stringof));
}
//----
Typeof A: Result
Typeof B: Result
[email protected](13): Result is different from Result
//----
Which is very strange. I found the root condition that recreates this: It is having a voldemort function that is qualified with inout:
//----
import std.stdio;
import std.conv;
auto S()
{
static struct Result
{
inout(Result) get() inout {return this;}
}
return Result();
}
void main()
{
auto a = S();
auto b = a.get();
alias typeof(a) A;
alias typeof(b) B;
writeln("Typeof A: ", A.stringof);
writeln("Typeof B: ", B.stringof);
assert(is(A == B), text(A.stringof, " is different from " , B.stringof));
}
//----
Typeof A: Result
Typeof B: Result
[email protected](21): Result is different from Result
//----
I'm marking this as major, because it is currently showing up in Phobos:
I wrote a fix for emplace, to bypass opAssign (which it should *not* be calling), and it turns out there is an emplace that fails to build a zip.opSlice because of this:
Pull: https://github.com/D-Programming-Language/phobos/pull/837
D2: http://d.puremagic.com/test-results/pull-history.ghtml?repoid=3&pullid=837
Comment #1 by monarchdodra — 2012-10-18T02:19:29Z
(In reply to comment #0)
> Which is very strange. I found the root condition that recreates this: It is
> having a voldemort function that is qualified with inout:
>
> //----
> import std.stdio;
> import std.conv;
>
> auto S()
> {
> static struct Result
> {
> inout(Result) get() inout {return this;}
> }
> return Result();
> }
>
> void main()
> {
> auto a = S();
> auto b = a.get();
> alias typeof(a) A;
> alias typeof(b) B;
> writeln("Typeof A: ", A.stringof);
> writeln("Typeof B: ", B.stringof);
> assert(is(A == B), text(A.stringof, " is different from " , B.stringof));
> }
> //----
> Typeof A: Result
> Typeof B: Result
> [email protected](21): Result is different from Result
> //----
Yeah, I forgot to mention, if you remove the "inout" attribute, or if you make Result a global struct, the problem disappears, so it *really* only ever appears when inout and voldemort are combined.
Comment #2 by code — 2012-10-18T02:29:59Z
This is probably the same issue I hit when merging the 2.060 frontend into LDC a while ago. The issue is that the same type can end up with two different mangled names, which causes two different TypeInfo instances to be emitted.
The test case from https://github.com/ldc-developers/ldc/blob/master/gen/tocall.cpp#L677:
---
auto iota() {
static struct Result {
this(int) {}
inout(Result) test() inout { return cast(inout)Result(0); }
}
return Result.init;
}
void main() { auto r = iota(); }
---
I'm not sure what the correct fix is here – Walter? Kenji?
Comment #3 by monarchdodra — 2012-10-28T04:01:55Z
(In reply to comment #2)
> This is probably the same issue I hit when merging the 2.060 frontend into LDC
> a while ago. The issue is that the same type can end up with two different
> mangled names, which causes two different TypeInfo instances to be emitted.
>
> [SNIP]
>
> I'm not sure what the correct fix is here – Walter? Kenji?
Any news? Do you have any idea about the *cost* of fixing this?
If there is a fix possible, then I'll wait for it (No problem waiting whatsoever). But if not, then I'll go for bypass, just want to know which direction I should take this...
Thanks.
This is a serious problem about the name mangling rule for Voldemort Type.
At least there is two rules.
1. Mangled name of a function symbol contains the mangled name of its return type.
2. A nested declaration's mangled name contains its enclosing mangled name.
---
module test;
pragma(msg, "1f: ", foo.mangleof);
// _D4test3fooFZAi
// --> _D [ 4test 3foo / [ FZ / Ai ] ] ...#1(Ai == int[])
int[] foo() {
struct S { int value; }
pragma(msg, "1i: ", S.mangleof);
// S4test3fooFZAi1S
// --> S [ 4test 3foo / [ FZ / Ai ] ] 1S ...#2
return null;
}
---
But, Voldemort Types cannot be mangled based on the rules.
Because, a nested struct requires enclosing function's mangling,
but the function requires return type's mangling. It's circular dependency.
In current, that is *accidentally* working.
---
auto bar() {
struct S { int value; }
pragma(msg, "2i: ", S.mangleof);
// S4test3bar1S
// --> S [ 4test 3bar / [ / ] ] 1S
// ...incorrect
return S(1);
}
pragma(msg, "2f: ", bar.mangleof);
// _D4test3barFZS4test3bar1S
// --> _D [ 4test 3bar / [ FZ / S4test3bar1S ] ]
// ...incorrect
---
And, inout type deduction on function call shoots the rule inconsistency.
---
auto baz(inout int = 0) {
struct S { int value; }
pragma(msg, "3i: ", S.mangleof); // S inside bar
// S4test3baz1S
// --> S [ 4test 3baz / [ / ] ] 1S ...(A)
return inout(S)(1);
}
pragma(msg, "3f: ", baz.mangleof);
// _D4test3bazFNgiZNgS4test3baz1S
// --> _D [ 4test 3baz / [ FNgiZ / Ng S4test3baz1S ] ]
pragma(msg, "3o: ", typeof(baz(0)).mangleof); // S outside bar
// S4test3bazFNgiZNgS4test3baz1S1S
// --> S [ 4test 3baz / [ FNgiZNg / S4test3baz1S ] ] 1S ...(B)
---
Compare:
A. --> S [ 4test 3baz / [ / ] ] 1S
B. --> S [ 4test 3baz / [ FNgiZNg / S4test3baz1S ] ] 1S
Mismatching between A and B is the root cause of this issue.
Comment #6 by github-bugzilla — 2012-12-17T21:38:47Z