digitalmars.D.bugs - [Issue 10487] New: Bad error message with assignment of const tuple
- d-bugmail puremagic.com (57/57) Jun 27 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10487
- d-bugmail puremagic.com (328/329) Jun 27 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10487
- d-bugmail puremagic.com (10/10) Jun 27 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10487
- d-bugmail puremagic.com (30/37) Jun 28 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10487
http://d.puremagic.com/issues/show_bug.cgi?id=10487 Summary: Bad error message with assignment of const tuple Product: D Version: D2 Platform: All OS/Version: All Status: NEW Keywords: diagnostic Severity: enhancement Priority: P2 Component: DMD AssignedTo: nobody puremagic.com ReportedBy: bearophile_hugs eml.cc I tag this as enhancement request, but it's borderline with being a bug report. This is a common programming mistake in D; using "auto" y becomes "const int" and not just an int, so later you can't assign to it the result of bar(): int bar() { return 0; } void spam(in int x) { auto y = x; // ... y = bar(); } void main() {} The error message is quite clear and easy to understand: test.d(7): Error: cannot modify const expression y But if I replace the type int with a Tuple: import std.typecons: Tuple; alias Foo = Tuple!int; Foo bar() { return Foo(); } void spam(in Foo x) { auto y = x; // ... y = bar(); } void main() {} DMD 2.064alpha gives me: test.d(9): Error: template std.typecons.Tuple!int.Tuple.opAssign does not match any function template declaration. Candidates are: ...\dmd2\src\phobos\std\typecons.d(504): std.typecons.Tuple!int.Tuple.opAssign(R)(auto ref R rhs) if (areCompatibleTuples!(typeof(this), R, "=")) test.d(9): Error: template std.typecons.Tuple!int.Tuple.opAssign(R)(auto ref R rhs) if (areCompatibleTuples!(typeof(this), R, "=")) cannot deduce template function from argument types !()(Tuple!int) The error messages are too much long, and they don't even talk about const tuples. I think those error messages should be improved. I expect future built-in D tuples to give an error message like the int case: test.d(7): Error: cannot modify const expression y -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 27 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10487 Andrej Mitrovic <andrej.mitrovich gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |andrej.mitrovich gmail.com 06:40:57 PDT --- Hmm, this is really quite strange. If we take a simple example: ----- import std.typecons; alias Foo = Tuple!int; void main() { const(Foo) f; f = f; } ----- We get the big template instance error you've posted. But if we copy-paste the Tuple implementation inline into the current module: ----- import std.array; import std.typetuple; import std.string; import std.format; private template Identity(alias T) { alias T Identity; } template Tuple(Specs...) { // Parse (type,name) pairs (FieldSpecs) out of the specified // arguments. Some fields would have name, others not. template parseSpecs(Specs...) { static if (Specs.length == 0) { alias TypeTuple!() parseSpecs; } else static if (is(Specs[0])) { static if (is(typeof(Specs[1]) : string)) { alias TypeTuple!(FieldSpec!(Specs[0 .. 2]), parseSpecs!(Specs[2 .. $])) parseSpecs; } else { alias TypeTuple!(FieldSpec!(Specs[0]), parseSpecs!(Specs[1 .. $])) parseSpecs; } } else { static assert(0, "Attempted to instantiate Tuple with an " ~"invalid argument: "~ Specs[0].stringof); } } template FieldSpec(T, string s = "") { alias T Type; alias s name; } alias parseSpecs!Specs fieldSpecs; // Used with staticMap. template extractType(alias spec) { alias spec.Type extractType; } template extractName(alias spec) { alias spec.name extractName; } // Generates named fields as follows: // alias Identity!(field[0]) name_0; // alias Identity!(field[1]) name_1; // : // NOTE: field[k] is an expression (which yields a symbol of a // variable) and can't be aliased directly. string injectNamedFields() { string decl = ""; foreach (i, name; staticMap!(extractName, fieldSpecs)) { decl ~= format("alias Identity!(field[%s]) _%s;", i, i); if (name.length != 0) { decl ~= format("alias _%s %s;", i, name); } } return decl; } // Returns Specs for a subtuple this[from .. to] preserving field // names if any. template sliceSpecs(size_t from, size_t to) { alias staticMap!(expandSpec, fieldSpecs[from .. to]) sliceSpecs; } template expandSpec(alias spec) { static if (spec.name.length == 0) { alias TypeTuple!(spec.Type) expandSpec; } else { alias TypeTuple!(spec.Type, spec.name) expandSpec; } } template areCompatibleTuples(Tup1, Tup2, string op) { enum areCompatibleTuples = isTuple!Tup2 && is(typeof( { Tup1 tup1 = void; Tup2 tup2 = void; static assert(tup1.field.length == tup2.field.length); foreach (i, _; Tup1.Types) { auto lhs = typeof(tup1.field[i]).init; auto rhs = typeof(tup2.field[i]).init; auto result = mixin("lhs "~op~" rhs"); } })); } struct Tuple { /** * The type of the tuple's components. */ alias staticMap!(extractType, fieldSpecs) Types; /** * Use $(D t.expand) for a tuple $(D t) to expand it into its * components. The result of $(D expand) acts as if the tuple components * were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a * single value.) * * Examples: * ---- * auto t = tuple(1, " hello ", 2.3); * writeln(t); // Tuple!(int, string, double)(1, " hello ", 2.3) * writeln(t.expand); // 1 hello 2.3 * ---- */ Types expand; mixin(injectNamedFields()); static if (is(Specs)) { // This is mostly to make t[n] work. alias expand this; } else { property ref Tuple!Types _Tuple_super() trusted { foreach (i, _; Types) // Rely on the field layout { static assert(typeof(return).init.tupleof[i].offsetof == expand[i].offsetof); } return *cast(typeof(return)*) &(field[0]); } // This is mostly to make t[n] work. alias _Tuple_super this; } // backwards compatibility alias field = expand; /** * Constructor taking one value for each field. Each argument must be * implicitly assignable to the respective element of the target. */ this()(Types values) { field[] = values[]; } /** * Constructor taking a compatible array. The array element type must * be implicitly assignable to each element of the target. * * Examples: * ---- * int[2] ints; * Tuple!(int, int) t = ints; * ---- */ this(U, size_t n)(U[n] values) if (n == Types.length && is(typeof({ foreach (i, _; Types) field[i] = values[i]; }))) { foreach (i, _; Types) { field[i] = values[i]; } } /** * Constructor taking a compatible tuple. Each element of the source * must be implicitly assignable to the respective element of the * target. */ this(U)(U another) if (areCompatibleTuples!(typeof(this), U, "=")) { field[] = another.field[]; } /** * Comparison for equality. */ bool opEquals(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "==")) { return field[] == rhs.field[]; } /// ditto bool opEquals(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "==")) { return field[] == rhs.field[]; } /** * Comparison for ordering. */ int opCmp(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "<")) { foreach (i, Unused; Types) { if (field[i] != rhs.field[i]) { return field[i] < rhs.field[i] ? -1 : 1; } } return 0; } /// ditto int opCmp(R)(R rhs) const if (areCompatibleTuples!(typeof(this), R, "<")) { foreach (i, Unused; Types) { if (field[i] != rhs.field[i]) { return field[i] < rhs.field[i] ? -1 : 1; } } return 0; } /** * Assignment from another tuple. Each element of the source must be * implicitly assignable to the respective element of the target. */ void opAssign(R)(auto ref R rhs) if (areCompatibleTuples!(typeof(this), R, "=")) { static if (is(R : Tuple!Types) && !__traits(isRef, rhs)) { if (__ctfe) { // Cannot use swap at compile time field[] = rhs.field[]; } else { // Use swap-and-destroy to optimize rvalue assignment swap!(Tuple!Types)(this, rhs); } } else { // Do not swap; opAssign should be called on the fields. field[] = rhs.field[]; } } /** * Takes a slice of the tuple. * * Examples: * ---- * Tuple!(int, string, float, double) a; * a[1] = "abc"; * a[2] = 4.5; * auto s = a.slice!(1, 3); * static assert(is(typeof(s) == Tuple!(string, float))); * assert(s[0] == "abc" && s[1] == 4.5); * ---- */ property ref Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() trusted if (from <= to && to <= Types.length) { return *cast(typeof(return)*) &(field[from]); } /** * Converts to string. */ string toString() { enum header = typeof(this).stringof ~ "(", footer = ")", separator = ", "; Appender!string w; w.put(header); foreach (i, Unused; Types) { static if (i > 0) { w.put(separator); } // TODO: Change this once toString() works for shared objects. static if (is(Unused == class) && is(Unused == shared)) formattedWrite(w, "%s", field[i].stringof); else { FormatSpec!char f; // "%s" formatElement(w, field[i], f); } } w.put(footer); return w.data; } } } alias Foo = Tuple!int; void main() { const(Foo) f; f = f; } ----- Then we get the simple error:Error: cannot modify const expression fIt looks like an implementation bug. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 27 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10487 bearophile_hugs eml.cc changed: What |Removed |Added ---------------------------------------------------------------------------- Severity|enhancement |normal Thank you Andrej. Now it's tagged as diagnostic dmd bug. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jun 27 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10487 Kenji Hara <k.hara.pg gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Component|DMD |Phobos Severity|normal |enhancementBut if we copy-paste the Tuple implementation inline into the current module:[snip]Then we get the simple error:No. It's not a compiler bug. Following code missing in copy-paste code: import std.traits; template isTuple(T) { static if (is(Unqual!T Unused : Tuple!Specs, Specs...)) { enum isTuple = true; } else { enum isTuple = false; } } ------------- std.typecons.Tuple defines opAssign method, so compiler always try to call it for assignment operation. So current error message is completely intended (even if it looks a little verbose). I change this to Phobos enhancement. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------Error: cannot modify const expression fIt looks like an implementation bug.
Jun 28 2013