Thread overview
Typeinfo inconsistencies
Aug 13, 2013
H. S. Teoh
Aug 13, 2013
Maxim Fomin
Aug 13, 2013
Maxim Fomin
August 13, 2013
So, I got some free time today and decided to look into issue 6210, and found out that the problem was caused by this:

	char[]        a = "h".dup;
	const(char)[] b = "h";
	string        c = "h";

	writeln(typeid(a).getHash(&a)); // prints 104
	writeln(typeid(b).getHash(&b)); // prints 703014434222502
	writeln(typeid(c).getHash(&c)); // prints 104

This is completely b0rken, because it causes the following code to fail:

	int[char[]] aa;
	aa["h"] = 1;

	assert(aa.dup == aa);	// fails

The reason for this inconsistency is that char[] and immutable(char)[]
(aka string) have their getHash functions overridden in druntime's
src/rt/typeinfo/ti_Ag.d, but there is no such override for
const(char)[].

I tried adding another subclass for const(char)[]'s typeid that inherits the correct version of getHash, but it didn't work because presumably this stuff is hardcoded into the compiler somewhere.

So my question is, where in the compiler decides to use the specific
typeinfos for char[] and immutable(char)[], but not const(char)[]?


T

-- 
That's not a bug; that's a feature!
August 13, 2013
On Tuesday, 13 August 2013 at 17:42:31 UTC, H. S. Teoh wrote:
> So, I got some free time today and decided to look into issue 6210, and
> found out that the problem was caused by this:
>
> 	char[]        a = "h".dup;
> 	const(char)[] b = "h";
> 	string        c = "h";
>
> 	writeln(typeid(a).getHash(&a)); // prints 104
> 	writeln(typeid(b).getHash(&b)); // prints 703014434222502
> 	writeln(typeid(c).getHash(&c)); // prints 104
>
> This is completely b0rken, because it causes the following code to fail:
>
> 	int[char[]] aa;
> 	aa["h"] = 1;
>
> 	assert(aa.dup == aa);	// fails
>
> The reason for this inconsistency is that char[] and immutable(char)[]
> (aka string) have their getHash functions overridden in druntime's
> src/rt/typeinfo/ti_Ag.d, but there is no such override for
> const(char)[].
>
> I tried adding another subclass for const(char)[]'s typeid that inherits
> the correct version of getHash, but it didn't work because presumably
> this stuff is hardcoded into the compiler somewhere.
>
> So my question is, where in the compiler decides to use the specific
> typeinfos for char[] and immutable(char)[], but not const(char)[]?
>
>
> T

It seems that important path is taken here: https://github.com/D-Programming-Language/dmd/blob/master/src/typinf.c#L138

And root of the issue maybe here: https://github.com/D-Programming-Language/dmd/blob/master/src/typinf.c#L793 (essentially if(char && immutable))

Based on ad-hoc dmd debug:

If type is "AssociativeArray!(const(char)[], int)" Type::builtinTypeInfo returns 0.

If type is "char[]" !t->vtinfo is false, so entire branch is skipped.

If type is "const(char)[]" then !t->builtinTypeInfo() is true and dmd executes COMDAT generation branch.

If type is "const(char)" then !t->builtinTypeInfo() is true and dmd executes COMDAT generation branch (also if (t->isConst()) is also true).

If type is "char" then "!t->builtinTypeInfo()" is false.

It seems that it is consistent with your observations.
August 13, 2013
On Tuesday, 13 August 2013 at 17:42:31 UTC, H. S. Teoh wrote:
> So, I got some free time today and decided to look into issue 6210, and
> found out that the problem was caused by this:
>
> 	char[]        a = "h".dup;
> 	const(char)[] b = "h";
> 	string        c = "h";
>
> 	writeln(typeid(a).getHash(&a)); // prints 104
> 	writeln(typeid(b).getHash(&b)); // prints 703014434222502
> 	writeln(typeid(c).getHash(&c)); // prints 104
>
> This is completely b0rken, because it causes the following code to fail:
>
> 	int[char[]] aa;
> 	aa["h"] = 1;
>
> 	assert(aa.dup == aa);	// fails
>
> The reason for this inconsistency is that char[] and immutable(char)[]
> (aka string) have their getHash functions overridden in druntime's
> src/rt/typeinfo/ti_Ag.d, but there is no such override for
> const(char)[].
>
> I tried adding another subclass for const(char)[]'s typeid that inherits
> the correct version of getHash, but it didn't work because presumably
> this stuff is hardcoded into the compiler somewhere.
>
> So my question is, where in the compiler decides to use the specific
> typeinfos for char[] and immutable(char)[], but not const(char)[]?
>
>
> T

OK

--- a/src/typinf.c
+++ b/src/typinf.c
@@ -790,7 +790,8 @@ int TypeDArray::builtinTypeInfo()
 #if DMDV2
     return !mod && (next->isTypeBasic() != NULL && !next->mod ||
         // strings are so common, make them builtin
-        next->ty == Tchar && next->mod == MODimmutable);
+        next->ty == Tchar && next->mod == MODimmutable ||
+        next->ty == Tchar && next->mod == MODconst);
 #else
     return next->isTypeBasic() != NULL;
 #endif

--- a/src/rt/typeinfo/ti_Ag.d
+++ b/src/rt/typeinfo/ti_Ag.d
@@ -186,3 +186,15 @@ class TypeInfo_Aya : TypeInfo_Aa
     }
 }

+// const(char)[]
+
+class TypeInfo_Axa : TypeInfo_Aa
+{
+    override string toString() const { return "const(char)[]"; }
+
+    override @property inout(TypeInfo) next() inout
+    {
+        return cast(inout)typeid(const(char));
+    }
+}
+

and this program runs fine and prints 104 for three cases

import std.stdio;

void main()
{
   int[char[]] aa;
   aa["h"] = 1;
   assert(aa.dup == aa);   // fails, now ok


   char[]        a = "h".dup;
   const(char)[] b = "h";
   string        c = "h";
   writeln(typeid(a).getHash(&a)); // prints 104
   writeln(typeid(b).getHash(&b)); // prints 703014434222502, now 104
   writeln(typeid(c).getHash(&c)); // prints 104
}