D BUGS Part 1 September 18 2008 This page lists 39 bugs/problems I have found in DMD V.1.035 (this page shows some things that aren't bugs too, but I think they have to be improved anyway). Probably some/many of such bugs/problems are already present in bugzilla. This page doesn't list: - Some other bugs I have found in the module system; - 2-4 more bugs I have found in the textual I/O. - Lot of (~20?) of problems/bugs I have found in format/writefln (I have fixed most of them in my printing functions, so fixing them is possible, and I can show my code too). I'll show the format/writefln problems/bugs in the next list of bugs/problems :-) -------------------------------------------- // 1 void main() { int[int][int] h = [1: [2: 3]]; } -------------------------------------------- // 2 void main() { int[string] h = ["a": 1, "bc": 2]; } -------------------------------------------- // 3 void main() { auto h = ["a": 1]; } -------------------------------------------- // 4 import std.stdio; void main() { auto a = ["a", "bc"]; writefln(a); // Prints: [[a],[b]] } -------------------------------------------- // 5 (this may not be considered really a bug) void main() { string s = "ab"; s.length += 1; } -------------------------------------------- // 6 void main() { int[int][] ah; ah.length = 1; } -------------------------------------------- // 7 void main() { int[int[1]] h; foreach(k, v; h) {} } -------------------------------------------- // 8 import std.stdio; void main() { int[] a1 = [1, 2, 3]; auto a3 = a1[] + 4; writefln(a3); // prints: [1,2,3,0,0,0,0] } -------------------------------------------- // 9 import std.stdio; void main() { int[] a1 = [1, 2, 3]; int[] a2 = [4, 5, 6]; int[] a3; a3[] = a1[] + a2[]; writefln(a3); // prints: [] } -------------------------------------------- // 10 import std.stdio; void main() { int[] a1 = [1]; int[] a2 = [2]; int[] a3; a3 = a1 + a2; } Gives wrong error message: // test.d(6): Error: Array operations not implemented -------------------------------------------- // 11 void main() { auto a = [10]; foreach(i, x, a) {} } This common programming mistake produces this error message that can be made more specific for the foreach(), to help the programmer: test.d(3): no identifier for declarator a (This mistake is caused because inside a foreach a comma often looks too much similar to a semicolon.) -------------------------------------------- // 12 void main() { int[] a; a.lenght = 2; } a .size property is shorter and avoids the very common typo like 'lenght': void main() { int[] a; a.size = 2; } -------------------------------------------- // 13 import std.stdio; void main() { std.stdio.writefn(1); writefln(2); // (A) } This program has to raise a compilation error at the line (A), because this line: import std.stdio; Has to import only the 'std.stdio' name in the current namespace. To import (discouraged practice) all names from a module a specific syntax may be invented, like: import std.stdio.*; void main() { writefln(1); std.stdio.writefn(2); // (B) } That imports all the names but not the module name itself, so the line (B) has to raise an error. -------------------------------------------- // 14 void main() { switch (1) { int x; case 1: assert(x == int.init); } } Here often x != x.init. -------------------------------------------- // 15 void main() { int[int] a1 = [1: 2]; int[int] a2 = [1: 2]; assert(a1 == a2); } (An equality test among AAs is very useful. Note: Python defines < <= == != >= > among AAs too, so a full cmpAA can be defined too.) -------------------------------------------- // 16 void main() { int[2][] a = [[1, 2]]; assert(a == [[1, 2]]); } I think this has to work. -------------------------------------------- // 17 struct Foo { int[1][] data; int opApply(int delegate(ref int[1]) dg) { int result; for (int i; i < data.length; i++) { auto aux data[i]; result = dg(aux); if (result) break; } return result; } } void main() {} Static arrays break the opApply/foreach protocol. -------------------------------------------- // 18 import std.stdio, std.boxer; void main() { auto a = new Box[1]; a[0] = box(a); writefln(a); // Error: Stack Overflow } Produces a stack overflow, the writefln isn't able to detect loops yet. In Python the print is able to detect loops: >>> l = [1, 2] >>> l[1] = l >>> print l [1, [...]] -------------------------------------------- // 19 template Bar(string name) { pragma(msg, name); // prints "int" instead of "x" } template Foo(alias A) { alias Bar!(A.stringof) Foo; } void main() { int x = 10; alias Foo!(x) Nothing; } -------------------------------------------- // 20 import std.c.stdlib: alloca; void main() { const int n = 8; for (int i; i < 2; i++) printf("%p\n", alloca(n)); } Error: it prints two times the same address. -------------------------------------------- // 21 void main() { int[1] a = [10]; int i; int[] b = new int[a[i]]; } -------------------------------------------- // 22 import std.stdio; void main() { int[int] aa1 = [1:2, 3:4]; int[int] aa2 = [5:6, 7:8]; byte[int[int]] s; s[aa1] = 1; s[aa2] = 2; writefln(s); // Prints: [[1:2,3:4]:2] } -------------------------------------------- // 23 import std.stdio; void main() { string[] words = ["how", "are", "you", "are"]; int[string] aa1; foreach (w; words) aa1[w] = ((w in aa1) ? (aa1[w] + 1) : 2); writefln(aa1); // Prints: [how:1,you:1,are:2] int[string] aa2; foreach (w; words) if (w in aa2) aa2[w]++; else aa2[w] = 2; writefln(aa2); // Prints: [how:2,you:2,are:3] } This can be a source of bugs in programs. -------------------------------------------- // 24 template IsArray(T) { const bool IsArray = is(typeof(T.length)) && is(typeof(T.sort)) && is(typeof(T.reverse)) && is(typeof(T.dup)); } TA[] foo(TA, TB)(TA[] a, TB b) { TA[] result; static if (IsArray!(TB)) { if (b.length == 0) { result = a; } else if (b.length == 1) { result = foo(a, b[0]); // line 12 } } return result; } void main() { foo("test", ""); } It raises errors like: test.d(14): Error: array index 0 is out of bounds b[0 .. 0] -------------------------------------------- // 25 In std.string the string functions startswith/endswith (or startsWith/endsWith) are useful, because to see if word2 is the start of another string word1 (that's a common operation) you have to do: if (word2.length >= word1.length && word2[0 .. word1.length] == word1) { Instead of a simpler (ispired from Python string methods): if (word2.startsWith(word1)) { If you forget the first part you will go out of the bounds, because in D slicing isn't safe a in Python. -------------------------------------------- // 26 A syntax for an optional stride can be added to slices/ranges: string1[10 .. 20 .. 2] = string2; foreach (i; 10..20..2) {...} They are quite useful. (the strided version copies the actual data). -------------------------------------------- // 27 Using 'and' and 'or' instead of && and || helps reduce some possible programmer mistakes, because the programmer may write & or | and the compiler may not catch the mistake. Using those keyword (as in Python and many other languages) avoids such mistakes, and C programmer will not be confused by this change because the || and && becomes deprecated, as the 'l' suffix for numbers. 'and' and 'or' are also quite less cryptic than && and || symbols. -------------------------------------------- // 28 void foo(SomeEnum e) {} enum SomeEnum { X } At compile time it prints 4 times: temp4.d(2): Error: enum SomeEnum is forward referenced temp4.d(2): Error: enum SomeEnum is forward referenced temp4.d(2): Error: enum SomeEnum is forward referenced temp4.d(2): Error: enum SomeEnum is forward referenced -------------------------------------------- // 29 The -v1 switch of DMD can probably be removed. -------------------------------------------- // 30 import std.stdio: writefln; void main() { alias int[2] T; writefln(T.init); } This program has to print [0,0] instead of 0 -------------------------------------------- // 31 import std.stdio; void main() { auto a = ["abc", "defg"]; writefln(a); } This code prints: [[a,b,c],[d,e,f]] But it must raise an error at compile time, because the final 'g' of the second array of char doesn't fit. -------------------------------------------- // 32 import std.stdio; void main() { string[] a = ["foo", "bar" "baz", "spam"]; writefln(a); } This code prints: [foo,barbaz,spam] But probably the programmer was meaning to create an array with 4 strings instead. D has the ~ concat operator, so to prevent possible programming bugs it's better to remove the implicit concat of strings separated by whitespace. Everywhere the programmer wants to concat strings the explicit concat operator can be used: string s = "this is a very long string that doesn't fit in" ~ " a line"; The "Python Zen" has a rule that says: Explicit is better than implicit. I think such rule is often good for the D language too (and Python has the same problem of Python here). -------------------------------------------- // 33 This works: void main() { int[string][] a1 = [["cc":0]]; } This doesn't work: void main() { int[string][] a2 = [["cc":0], ["DD":10]]; } Gives: temp2.d(2): Error: array initializers as expressions are not allowed -------------------------------------------- // 34 This code: /// this is a ddoc comment void foo(int i) { printf("%d\n", i); } /// ditto void foo(uint i) { printf("%d\n", i); } /// ditto void foo(short i) { printf("%d\n", i); } Generates this correct HTML documentation: void foo(int i); void foo(uint i); void foo(short i); this is a ddoc comment Then I have tried to generate those similar functions with a mixin(), but then I can't find a way to make ddoc list them (into the generated Html) grouped: /// this is a ddoc comment void foo(int i) { printf("int) %d\n", i); } template Gen(string type) { const Gen = ` /// ditto void foo(` ~ type ~ ` i) { printf("` ~ type ~ `) %d\n", i); }`; } /// ditto mixin(Gen!("uint")); /// ditto mixin(Gen!("short")); void main() { foo(10); foo(cast(uint)20); foo(cast(short)30); } -------------------------------------------- // 35 void main() { int[][int] aa1 = [10: [1, 2, 3], 20: [10, 20]]; } I have specified int[] on the left, so this error message sounds out of place: // temp.d(2): Error: cannot implicitly convert expression ([10,20]) of type int[2u] to int[3u] -------------------------------------------- // 36 This shows a bug in module system, I have four three modules, named main_module, mod1, mod2: module main_module; import mod1, mod2; void main() { string s = "hello".toupper(); } ---------- module mod1; import std.ctype: toupper; ---------- module mod2; import std.string: toupper; The compilation of main_module produces this error: main_module.d(4): Error: mod1.toupper at mod1.d(2) conflicts with mod2.toupper at mod2.d(2) While this single module compiles: import std.ctype: toupper; import std.string: toupper; void main() { string s = "hello".toupper(); } -------------------------------------------- // 37 import std.stream: BufferedFile, FileMode; import std.string: toString; void main() { auto fout = new BufferedFile("foo.txt", FileMode.Out); int x = 10; fout.write(toString(x) ~ "\n"); fout.close(); } Generates this foo.txt file (bytes expressed in hexdecimal values): 03 00 00 00 31 30 0A -------------------------------------------- // 38 > < operators among box arrays are wrong, the following program asserts: import std.boxer, std.stdio; void main() { auto a1 = boxArray(10, 45.4); auto a2 = boxArray(10, 45.2); assert(a1 > a2); } -------------------------------------------- // 39 To see this module system problem you need 3 source files, named test.d, a.d, b.d: ------- The a.d module: import std.string: join; import std.string: join; ------- The b.d module: import std.string: join; ------- The main module is test.d: import a, b; void main() { join(["a"], ""); } DMD v1.024 produces this: test.d(4): Error: a.join at a.d(1) conflicts with b.join at b.d(1) Is this a bug? Aren't names qualified imports private too? And if not, why? --------------------------------------------