digitalmars.D - Compiler bug? (alias sth this; and std.signals)
- Joe (57/57) Nov 12 2012 Ok, i was trying out D and wanted to see if i could create
- Joe (3/60) Nov 13 2012 No one? Is this a bug, or am I overlooking something? (entirely
- Maxim Fomin (7/7) Nov 13 2012 The third version using dmd 2.060 linux crashes. Valgrind shows
- eskimo (12/13) Nov 13 2012 Property is a struct and thus it is passed by value, which means that
- Joe (5/8) Nov 14 2012 But wait! Due to "alias get this;", f.prop shouldn't copy prop
- eskimo (12/18) Nov 14 2012 It should copy f.prop and it does. Your check results have another
- eskimo (10/10) Nov 14 2012 In your particular case, which this definitely is a bug and if only for
Ok, i was trying out D and wanted to see if i could create something that behaves like a property but adds a change notification signal. Doing this, I encountered a hangup which might be a bug. So far I have the following: ----------------------------------------------------------- import std.signals; import std.stdio; struct Property { alias get this; int set(int v) { emit(); return data_ = v; } int get() { return data_; } int opAssign(int v) { return set(v); } mixin Signal!(); private: int data_ = 0; } struct Foo { Property prop; } class Observer { void watch() { writeln("Value change observed!"); } } ----------------------------------------------------------- This works: void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; } This also works: void main() { Foo f; Observer o = new Observer; f.prop = 7; writeln(f.prop); } This never terminates: void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; writeln(f.prop); }
Nov 12 2012
No one? Is this a bug, or am I overlooking something? (entirely possible since I didn't use D since trying it once in D1 times) On Monday, 12 November 2012 at 11:59:50 UTC, Joe wrote:Ok, i was trying out D and wanted to see if i could create something that behaves like a property but adds a change notification signal. Doing this, I encountered a hangup which might be a bug. So far I have the following: ----------------------------------------------------------- import std.signals; import std.stdio; struct Property { alias get this; int set(int v) { emit(); return data_ = v; } int get() { return data_; } int opAssign(int v) { return set(v); } mixin Signal!(); private: int data_ = 0; } struct Foo { Property prop; } class Observer { void watch() { writeln("Value change observed!"); } } ----------------------------------------------------------- This works: void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; } This also works: void main() { Foo f; Observer o = new Observer; f.prop = 7; writeln(f.prop); } This never terminates: void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; writeln(f.prop); }
Nov 13 2012
The third version using dmd 2.060 linux crashes. Valgrind shows lots of errors (other two are clean) relating to corrupted heap caused likely here (https://github.com/D-Programming-Language/phobos/blob/master std/signals.d#L248) which is not surprising judging by semi-manual memory management in __dtor. I guess either you are abusing library feature or found a bug. Consider posting this to Bugzilla.
Nov 13 2012
Not a compiler bug. A bug in the implementation of std.signals.Property is a struct and thus it is passed by value, which means that the signal is copied. Signal does not define a postblit constructor, because it was intended to be used in classes, so a bitwise copy is done. Meaning that the internal allocated storage is freed twice. If used in a class there is no issue with copying a signal, because it is a mixin and does not exist on its own and an object has reference semantics. I am already working on a replacement for std.signals which fixes some of its shortcomings. It will also fix this one. Best regards, Robertwriteln(f.prop);
Nov 13 2012
On Tuesday, 13 November 2012 at 22:55:38 UTC, eskimo wrote:Property is a struct and thus it is passed by value, which means that the signal is copied.But wait! Due to "alias get this;", f.prop shouldn't copy prop but call get (which it does in the working - second - case)! How to check? Remove the alias and writeln(f.prop) prints "Property(7)", with the alias writeln(f.prop) prints "7"
Nov 14 2012
But wait! Due to "alias get this;", f.prop shouldn't copy prop but call get (which it does in the working - second - case)!-> Also here the struct is copied, but the signal has no content yet and thus no memory allocation yet occurred. (So no double free)How to check? Remove the alias and writeln(f.prop) prints "Property(7)", with the alias writeln(f.prop) prints "7"It should copy f.prop and it does. Your check results have another explanation: writefln is a complete generic templated function, it has no way of knowing that you want to pass the contained integer instead of the struct. So by default it takes the struct (it has no requirements that would lead to trigger the alias this). Eventually it will call some function taking concrete arguments for printing the data. This concrete function is not overloaded for your struct so the alias this to int triggers now and the overload for int is being called. But first it is copied to every generic function that might be called on the way.
Nov 14 2012
On Wednesday, 14 November 2012 at 09:31:47 UTC, eskimo wrote:But first it is copied to every generic function that might be called on the way.Ok, I guess it just doesn't do what I understood it to do (which is too bad, but to be expected with a new language). In any case you would appear to be correct, as void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; writeln(f.prop.get); } works. It just doesn't look like intended.
Nov 15 2012
On Thu, 2012-11-15 at 09:53 +0100, Joe wrote:On Wednesday, 14 November 2012 at 09:31:47 UTC, eskimo wrote:Well if signal had a proper postblit constructor your original way of doing it would work. It is just not as efficient, but this is a price you have to pay. The compiler has no way of knowing that you intended to pass the contained int from the beginning, when you are actually passing the containing struct. But, considering that the alias this triggers only when you are issuing an operation not supported by the struct itself, it is pretty reasonable behaviour and everything else would be pretty surprising. Best regards, RobertBut first it is copied to every generic function that might be called on the way.Ok, I guess it just doesn't do what I understood it to do (which is too bad, but to be expected with a new language). In any case you would appear to be correct, as void main() { Foo f; Observer o = new Observer; f.prop.connect(&o.watch); f.prop = 7; writeln(f.prop.get); } works. It just doesn't look like intended.
Nov 15 2012
On Thursday, 15 November 2012 at 09:37:55 UTC, eskimo wrote:But, considering that the alias this triggers only when you are issuing an operation not supported by the struct itself, it is pretty reasonable behaviour and everything else would be pretty surprising. Best regards, RobertI wonder though why it works at all then, because without the alias the string conversion *is* supported and produces "Property(7)".
Nov 15 2012
:-) Indeed, that is the only thing that surprised me too (but not as much as in another language, because of D's capabilities). The solution I think is this overload found in std.format of formatValue: void formatValue(Writer, T, Char)(Writer w, auto ref T val, ref FormatSpec!Char f) if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || ! isBuiltinType!T) && !is(T == enum)) -> Its body implements the generic print for structs. It either calls the structs toString() method if available or if it is a range it uses formatRange() otherwise it prints its type name with its contained values. But as you can see the templates requirement states !isBuiltinType!T, so in case of your alias this to an int, it won't be used. So the implementer of this method most likely had taken into account the possibility of an alias this to a built in type. Btw., I love D's readability, it was really easy to find this and to understand what it does. Best regards, Robert On Thu, 2012-11-15 at 15:11 +0100, Joe wrote:I wonder though why it works at all then, because without the alias the string conversion *is* supported and produces "Property(7)".
Nov 15 2012
Why would is(T == struct) be true but !isBuiltinType!T be false? This seems highly inconsistent. If T is a struct, it is not a builtin type, and if T is int (also making the condition false), then Property should never have been passed as a struct, but as the int gotten via the alias. On Thursday, 15 November 2012 at 14:52:41 UTC, eskimo wrote::-) Indeed, that is the only thing that surprised me too (but not as much as in another language, because of D's capabilities). The solution I think is this overload found in std.format of formatValue: void formatValue(Writer, T, Char)(Writer w, auto ref T val, ref FormatSpec!Char f) if ((is(T == struct) || is(T == union)) && (hasToString!(T, Char) || ! isBuiltinType!T) && !is(T == enum)) -> Its body implements the generic print for structs. It either calls the structs toString() method if available or if it is a range it uses formatRange() otherwise it prints its type name with its contained values. But as you can see the templates requirement states !isBuiltinType!T, so in case of your alias this to an int, it won't be used. So the implementer of this method most likely had taken into account the possibility of an alias this to a built in type. Btw., I love D's readability, it was really easy to find this and to understand what it does. Best regards, Robert
Nov 16 2012
Why would is(T == struct) be true but !isBuiltinType!T be false?Exactly because of alias this. Your type is a struct and an integer at the same time, so to speak. At least it behaves that way and that's what its all about. If if looks like a duck, quacks like a duck, ... it is a duck ;-)This seems highly inconsistent. If T is a struct, it is not a builtin type, and if T is int (also making the condition false), then Property should never have been passed as a struct, but as the int gotten via the alias.Your struct is both at the same time, it stops being both as soon as it is copied to a function argument expecting an int, but not any sooner. It is always passed as the struct, that's also what you are doing: You pass the struct to writefln. It only allows being treated as an int, which also means it can be copied to an int argument of a function. But this happens even later in the call chain, the above mentioned function is also a template, so it sees the struct not the naked int, but it does the expected thing because of the seemingly contradictory checks. So another overload is used, which expects builtin types. With your alias this your struct is a struct and a builtin at the same time. Without the alias this feature, you would be right that the check makes no sense.
Nov 16 2012
In your particular case, which this definitely is a bug and if only for missing documentation that you should only use it from classes, you might regardless be better of with writeln(f.prop.get) because you avoid the needless copy with memory allocation/deallocation (if done correctly) of the signal. Passing f.prop directly should be possible for any function that takes an int, but not for generic template functions because then you pass the struct directly. Best regards, Robert
Nov 14 2012