[Issue 10487] Bad error message with assignment of const tuple
d-bugmail at puremagic.com
d-bugmail at puremagic.com
Thu Jun 27 06:41:00 PDT 2013
http://d.puremagic.com/issues/show_bug.cgi?id=10487
Andrej Mitrovic <andrej.mitrovich at gmail.com> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |andrej.mitrovich at gmail.com
--- Comment #1 from Andrej Mitrovic <andrej.mitrovich at gmail.com> 2013-06-27 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 f
It looks like an implementation bug.
--
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
More information about the Digitalmars-d-bugs
mailing list