string[string][] Dict; //sure ok.
alias string[string][] dict; //Error
void main()
{
Dict = [["Cow":"moo" ],["Duck":"quack"]];//cool
Dict ~= ["Dog":"woof"]; //No prob.
assert(Dict==[["Cow":"moo"],["Duck":"quack"],["Dog":"woof"]]);//looks legit
//dict temp = [["Cow":"moo" ],["Duck":"quack"]];//Error
//string[string][] temp2 = [["Cow":"moo" ],["Duck":"quack"]];//Error
//And My favorite one of all:
//auto temp2 = [["Cow":"moo"],["Duck":"quack"]]; //Error
auto temp3 = tuple([["Cow":"moo"]]);//works. Variant as well.
}
With everything commented out, Dict works just fine from way up there.
But Tuple, Variant, tls,
Those are the only way to get this to work.
The specific error it gives is:
Error: Integer constant expected instead of "Cow"
Comment #1 by bearophile_hugs — 2012-08-18T05:51:44Z
A little reformatted:
import std.typecons: tuple;
string[string][] aa1; // OK
alias string[string][] AA;
void main() {
aa1 = [["A": "B" ], ["C": "D"]]; // OK
aa1 ~= ["E": "F"]; // OK
assert(aa1 == [["A": "B"], ["A": "B"], ["E": "F"]]); // OK
auto a2 = tuple([["A": "B"]]); // OK
AA a3 = [["A": "B" ], ["C": "D"]]; // error
string[string][] a4 = [["A": "B" ], ["C": "D"]]; // error
auto a5 = [["A": "B"], ["C": "D"]]; // error
}
It gives the errors:
test.d(9): Error: Integer constant expression expected instead of "A"
test.d(9): Error: Integer constant expression expected instead of "A"
test.d(9): Error: Integer constant expression expected instead of "C"
test.d(9): Error: Integer constant expression expected instead of "C"
test.d(9): Error: cannot implicitly convert expression ([["B"],["D"]]) of type string[][] to string[string][]
test.d(10): Error: Integer constant expression expected instead of "A"
test.d(10): Error: Integer constant expression expected instead of "A"
test.d(10): Error: Integer constant expression expected instead of "C"
test.d(10): Error: Integer constant expression expected instead of "C"
test.d(10): Error: cannot implicitly convert expression ([["B"],["D"]]) of type string[][] to string[string][]
test.d(11): Error: Integer constant expression expected instead of "A"
test.d(11): Error: Integer constant expression expected instead of "A"
test.d(11): Error: Integer constant expression expected instead of "C"
test.d(11): Error: Integer constant expression expected instead of "C"
Comment #2 by k.hara.pg — 2012-08-18T13:23:42Z
This might be a dup of bug 5448.
Comment #3 by 10equals2 — 2012-08-18T19:36:09Z
(In reply to comment #2)
> This might be a dup of bug 5448.
It is not *exactly* the same, but they do overlap.
Thank you for pointing that out.
Something not mentioned in bug 5448 is that alias does not work with AA,
Although that might be implied.
If you feel that this cuts the difference too close, either close it or I will.
I apologize, I didn't quite understand all of the reasons it would not work when I opened the ticket.
Comment #4 by k.hara.pg — 2012-08-19T01:03:40Z
(In reply to comment #3)
> (In reply to comment #2)
> > This might be a dup of bug 5448.
>
> It is not *exactly* the same, but they do overlap.
> Thank you for pointing that out.
>
> Something not mentioned in bug 5448 is that alias does not work with AA,
> Although that might be implied.
>
> If you feel that this cuts the difference too close, either close it or I will.
>
> I apologize, I didn't quite understand all of the reasons it would not work
> when I opened the ticket.
To me, both this and bug 5448 have the same root that a problem of semantic analysis for the initializer of variable declaration.
Comment #6 by bearophile_hugs — 2013-11-24T08:38:48Z
(In reply to comment #5)
> Most of these were done by issue 5448, except a5.
>
> https://github.com/D-Programming-Language/dmd/pull/2876
Is that also fixing this?
void main() {
int[char][char] foo1 = ['A': ['B': 1]]; //rejects-valid
int[char][char] foo2 = cast()['A': ['B': 1]]; //workaround
}
Comment #7 by yebblies — 2013-11-24T08:49:50Z
(In reply to comment #6)
>
> void main() {
> int[char][char] foo1 = ['A': ['B': 1]]; //rejects-valid
> int[char][char] foo2 = cast()['A': ['B': 1]]; //workaround
> }
Nope. This patch only affects places where the type is inferred.
Comment #8 by yebblies — 2013-11-24T21:15:12Z
Ok I added those cases too.
Comment #9 by bearophile_hugs — 2013-11-24T23:54:54Z
(In reply to comment #8)
> Ok I added those cases too.
Thank you, such small things improve the usability of D a lot.
Comment #10 by yebblies — 2013-11-25T00:24:11Z
(In reply to comment #9)
> (In reply to comment #8)
> > Ok I added those cases too.
>
> Thank you, such small things improve the usability of D a lot.
It's actually turned out worse than I thought.
I think we should change the rules a little. These test cases should all compile, and the ones that don't currently work are commented out.
- If the type is given try and match it
- Else if each element has a key, default is AA
otherwise is array
- Vectors and pointers are treated the same as dynamic arrays
- For static array inits with keys, the length is given by the static array
- For dynamic array inits with keys, the length is given by the largest key
void main()
{
{ // normal array literal
int[] a = [1, 2, 3];
int[3] b = [1, 2, 3];
static assert(!is(typeof({ int[int] c = [1, 2, 3]; }))); // need key for each element
auto d = [1, 2, 3];
static assert(is(typeof(d) == int[])); // default is dynamic array
}
{ // some keys, no gaps
int[] a = [1 : 2, 3, 0 : 1];
int[3] b = [1 : 2, 3, 0 : 1];
static assert(!is(typeof({ int[int] c = [1 : 2, 3, 0 : 1]; }))); // need key for each element
// auto d = [1 : 2, 3, 0 : 1];
// static assert(is(typeof(d) == int[])); // default to array when not enough keys
}
{ // all keys, no gaps
int[] a = [1 : 2, 2 : 3, 0 : 1];
int[3] b = [1 : 2, 2 : 3, 0 : 1];
int[int] c = [1 : 2, 2 : 3, 0 : 1];
auto d = [1 : 2, 2 : 3, 0 : 1];
static assert(is(typeof(d) == int[int])); // default to AA when has all keys
}
{ // some keys, gap
int[] a = [1 : 2, 3];
int[3] b = [1 : 2, 3];
// auto c = [1 : 2, 3];
// static assert(is(typeof(c) == int[])); // should fill in gaps with int.init
}
{ // value is AA
// int[int][int] a = [0 : [0 : 3]];
int[][int] b = [0 : [0 : 3]];
int[1][int] c = [0 : [0 : 3]];
// int[int][] d = [0 : [0 : 3]];
// int[int][1] e = [0 : [0 : 3]];
auto f = [0 : [0 : 3]];
// static assert(is(typeof(f) == int[int][int])); // default to AA when has all keys
}
{ // key is AA
int[int[int]] a = [[0 : 0] : 0];
// int[int[]] b = [[0 : 0] : 0];
// int[int[1]] c = [[0 : 0] : 0];
auto d = [[0 : 0] : 0];
static assert(is(typeof(d) == int[int[int]]));
}
{ // value is array
int[][int] a = [0 : [2, 3]];
int[][] b = [0 : [2, 3]];
int[2][int] c = [0 : [2, 3]];
int[2][] d = [0 : [2, 3]];
auto e = [0 : [2, 3]];
static assert(is(typeof(e) == int[][int]));
}
{ // key is array
int[int[]] a = [[2, 3] : 0];
int[int[2]] b = [[2, 3] : 0];
auto c = [[2, 3] : 0];
static assert(is(typeof(c) == int[int[]]));
}
{ // value has gap
// int[][int] a = [0 : [1 : 2, 3]];
int[][] b = [0 : [1 : 2, 3]];
// int[3][int] c = [0 : [1 : 2, 3]];
int[3][] d = [0 : [1 : 2, 3]];
// auto e = [0 : [1 : 2, 3]];
// static assert(is(typeof(e) == int[][int]));
}
{ // key has gap
// int[int[]] a = [[1 : 2, 3] : 0];
// int[int[3]] b = [[1 : 2, 3] : 0];
// auto c = [[1 : 2, 3] : 0];
// static assert(is(typeof(c) == int[int[]]));
}
}
Comment #11 by bearophile_hugs — 2013-11-25T01:22:48Z
(In reply to comment #10)
> It's actually turned out worse than I thought.
>
> I think we should change the rules a little. These test cases should all
> compile, and the ones that don't currently work are commented out.
>
> - If the type is given try and match it
> - Else if each element has a key, default is AA
> otherwise is array
> - Vectors and pointers are treated the same as dynamic arrays
> - For static array inits with keys, the length is given by the static array
> - For dynamic array inits with keys, the length is given by the largest key
>
> void main()
> {
> { // normal array literal
> int[] a = [1, 2, 3];
> int[3] b = [1, 2, 3];
> static assert(!is(typeof({ int[int] c = [1, 2, 3]; }))); // need key
> for each element
> auto d = [1, 2, 3];
> static assert(is(typeof(d) == int[])); // default is dynamic array
> }
> { // some keys, no gaps
> int[] a = [1 : 2, 3, 0 : 1];
> int[3] b = [1 : 2, 3, 0 : 1];
> static assert(!is(typeof({ int[int] c = [1 : 2, 3, 0 : 1]; }))); //
> need key for each element
> // auto d = [1 : 2, 3, 0 : 1];
> // static assert(is(typeof(d) == int[])); // default to array when not
> enough keys
Perhaps related, a report from 2010-08-21:
https://d.puremagic.com/issues/show_bug.cgi?id=4703
Comment #12 by bearophile_hugs — 2013-11-27T17:41:12Z
Another case:
void main() {
int[][int] aa1;
aa1 = [1: [2, 3], 4: []]; // OK
int[int][int] aa2;
aa2 = [1: [2: 3], 4: []]; // error
}
test.d(5): Error: incompatible types for (([2:3]) : ([])): 'int[int]' and 'void[]'
Comment #13 by k.hara.pg — 2013-11-27T23:20:26Z
This issue contains one compiler implementation bug and two enhancements.
----
In a variable definition which has explicit type declaration, the 'target' type should be applied to the initializer recursively.
void testBugs()
{
{
int[ ] da = [1:2, 3]; // OK
int[3] sa = [1:2, 3]; // OK
int[int] aa = [0:3]; // OK
}
{
int[ ][int] daa = [0:[1:2, 3]]; // NG, but should be OK
int[3][int] saa = [0:[1:2, 3]]; // NG, but should be OK
int[int][int] aaa = [0:[0:3]]; // NG, but should be OK
}
}
This is a compiler implementation bug.
----
In current D grammar, ArrayInitializer can optionally have index (==AA key) part,
http://dlang.org/declaration#ArrayInitializer
ArrayInitializer:
[ ]
[ ArrayMemberInitializations ]
ArrayMemberInitializations:
ArrayMemberInitialization
ArrayMemberInitialization ,
ArrayMemberInitialization , ArrayMemberInitializations
ArrayMemberInitialization:
NonVoidInitializer
AssignExpression : NonVoidInitializer
Let's classify the pattern of array initializer.
1. Bare array initializer
eg. [1, 2, 3]
2. Sparse array initializer
eg. [1:2, 3, 0:1], ["1":"a", "b"]
(gap is not an issue)
3. Indexed array initializer
eg. [1:1, 2:2, 3:3], ["1":"a", "2":"b"]
Sparse version is only allowed for dynamic/static array initializing, because AA initializing needs key for each elements.
void testInitializer1()
{
// Bare array literal
{
int[] a = [1, 2, 3];
int[3] b = [1, 2, 3];
static assert(!is(typeof({
int[int] c = [1, 2, 3]; // need key for each element
})));
}
// Sparse array initializer is allowed only for (s)array.
{
int[] a = [1:2, 3, 0:1];
int[3] b = [1:2, 3, 0:1];
static assert(!is(typeof({
int[int] c = [1:2, 3, 0:1]; // need key for each element
})));
}
// Indexed array initializer
{
int[] a = [1:2, 2:3, 0:1];
int[3] b = [1:2, 2:3, 0:1];
int[int] c = [1:2, 2:3, 0:1];
}
}
However, the index part in ArrayInitializer should be AssignExp,
so sparse array initializer cannot be appeared in index part.
void testInitializer2()
{
// value is array (== indexed array initializer)
{
int[][int] a = [0:[2, 3]];
int[][] b = [0:[2, 3]];
int[2][int] c = [0:[2, 3]];
int[2][] d = [0:[2, 3]];
}
// value has gap (== sparse array initializer)
{
int[][int] a = [0:[1:2, 3]];
int[][] b = [0:[1:2, 3]];
int[3][int] c = [0:[1:2, 3]];
int[3][] d = [0:[1:2, 3]];
}
// value is AA (== indexed array initializer)
{
int[int][int] a = [0:[0:3]];
int[][int] b = [0:[0:3]];
int[1][int] c = [0:[0:3]];
int[int][] d = [0:[0:3]];
int[int][1] e = [0:[0:3]];
}
// key is array (== array literal expression == AssignExp)
{
int[int[]] a = [[2, 3]:0];
int[int[2]] b = [[2, 3]:0];
}
// key is AA (currently not supported in grammar)
{
int[int[int]] a = [[0:0]:0];
//int[int[]] b = [[0:0]:0]; // [0:0] is not AssignExp
//int[int[1]] c = [[0:0]:0]; // [0:0] is not AssignExp
}
// key has gap (currently not supported in grammar)
{
//int[int[]] a = [[1:2, 3]:0];
//int[int[3]] b = [[1:2, 3]:0];
}
}
If we change the grammar as follows, the commented out lines in testInitializer2 will be accepted properly.
ArrayMemberInitialization:
NonVoidInitializer
Initializer : NonVoidInitializer <---!
It is the first enhancement request.
----
Type inference is in completely different situation.
At first, compiler tries to translate initializer to expression. Then,
- if it is impossible, inference fails.
- otherwise, pick up the type of converted expression.
An important point is, currently compiler does not convert sparse array initializer to corresponding array literal expression.
void testInference()
{
// bare array initializer to array literal
{
auto a = [1, 2, 3];
static assert(is(typeof(a) == int[]));
}
// sparse array initializer to array literal
// (gap is not important)
{
//auto a = [1:2, 3, 0:1];
//static assert(is(typeof(a) == int[]));
//auto a = [1:2, 3];
//static assert(is(typeof(a) == int[]));
}
// indexed array literal to associative array literal
{
auto a = [1:2, 2:3, 0:1];
static assert(is(typeof(a) == int[int]));
}
}
Supporting it is the second enhancement request.
Comment #14 by bearophile_hugs — 2013-11-28T00:35:09Z
(In reply to comment #13)
> This issue contains one compiler implementation bug and two enhancements.
This is from Issue 4703 :
void foo(int[] a) {}
void bar(int[int] aa) {}
void main() {
int[] a = [1:2, 3:4, 5:6];
int[int] aa = [1:2, 3:4, 5:6];
foo(a);
bar(aa);
foo([1:2, 3:4, 5:6]); // error
bar([1:2, 3:4, 5:6]); // OK
}
Comment #15 by bearophile_hugs — 2013-11-28T00:55:17Z
Here I think both should compile or both should not compile:
void main() {
enum Foo { A }
int[] a1 = [Foo.A: 10]; // OK
int[] a2; a2 = [Foo.A: 10]; // Error
}
Currently in dmd 2.065alpha that program produces:
test.d(4): Error: cannot implicitly convert expression ([cast(Foo)0:10]) of type int[Foo] to int[]
Comment #16 by bearophile_hugs — 2013-11-28T01:51:49Z
(In reply to comment #15)
> Here I think both should compile or both should not compile:
>
>
> void main() {
> enum Foo { A }
> int[] a1 = [Foo.A: 10]; // OK
> int[] a2; a2 = [Foo.A: 10]; // Error
> }
I think I'd like to both compile.
Comment #17 by robert.schadek — 2024-12-13T18:01:06Z