digitalmars.D.learn - Assigning a static array
- Brad Anderson (18/18) Apr 18 2013 Is this supposed to be allowed:
- Brad Anderson (19/19) Apr 18 2013 For reference, here was what user soos on IRC was doing that
- Jonathan M Davis (18/39) Apr 18 2013 Yes, that's legal, though it should arguably be @system, since it's doin...
- bearophile (10/37) Apr 18 2013 It's currently a warning, and it will be required.
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (34/52) Apr 18 2013 There is a similar problem with the automatically generated array argume...
- Steven Schveighoffer (32/66) Apr 18 2013 There is no guarantee that the incoming array from a variadic function i...
- bearophile (9/33) Apr 18 2013 To avoid those bugs I have suggested the simpler possible thing:
- Steven Schveighoffer (7/13) Apr 19 2013 I am OK with this as long as it is done AFTER scope works (and I would
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (30/50) Apr 18 2013 Interesting.
- Steven Schveighoffer (12/17) Apr 19 2013 [snip]
- H. S. Teoh (13/56) Apr 18 2013 [...]
- Steven Schveighoffer (5/10) Apr 19 2013 I like bearophile's solution, we increasingly need scope to work for D t...
- bearophile (5/7) Apr 19 2013 But I don't see any plan/implementation timeframe for "scope" :-(
- bearophile (5/7) Apr 19 2013 I have added a note about scope:
- bearophile (23/45) Apr 18 2013 Yes, this is supposed to be allowed with the current design of D.
- Brad Anderson (2/19) Apr 18 2013 That's good to hear, Bearophile. Thanks Kenji.
Is this supposed to be allowed: ubyte[] a; ubyte[16] b; a = b; assert(a.ptr == b.ptr); Because if so that makes it terribly easy to do a bug like this (as I just saw in IRC): struct A { ubyte[] a; this(ubyte c) { ubyte[16] b; b[] = c; this.a = b; // a now points at an immediately invalid static array } }
Apr 18 2013
For reference, here was what user soos on IRC was doing that import std.digest.md; import std.stdio; struct Hash { ubyte[] hash1; ubyte[] hash2; this (string str) { auto md5 = new MD5Digest(); this.hash1 = md5.digest(str); this.hash2 = digest!MD5(str); } }; void main() { auto h = Hash("hello world!"); writeln(toHexString(h.hash1)); writeln(toHexString(h.hash2)); } It's not obvious at all what the problem is.
Apr 18 2013
On Thursday, April 18, 2013 23:06:32 Brad Anderson wrote:Is this supposed to be allowed: ubyte[] a; ubyte[16] b; a = b; assert(a.ptr == b.ptr); Because if so that makes it terribly easy to do a bug like this (as I just saw in IRC): struct A { ubyte[] a; this(ubyte c) { ubyte[16] b; b[] = c; this.a = b; // a now points at an immediately invalid static array } }Yes, that's legal, though it should arguably be system, since it's doing essentially the same thing as int i; int* p = &i; which _is_ system. The compiler doesn't currently consider slicing a static array in general to be system though, much as it should ( http://d.puremagic.com/issues/show_bug.cgi?id=8838 ). I could see an argument that it should have to be a = b[]; so that the slicing is explicit instead of just a = b; where it's implicit, but AFAIK, that's not currently required. You have to be very careful when slicing static arrays. If it were up to me, exlicit slicing would _always_ be required when slicing a static array, even when calling functions which take a dynamic array, but that's not how it works unfortunately. - Jonathan M Davis
Apr 18 2013
Jonathan M Davis:I could see an argument that it should have to be a = b[]; so that the slicing is explicit instead of just a = b; where it's implicit, but AFAIK, that's not currently required.It's currently a warning, and it will be required. -------------------------- Ali Çehreli:There is a similar problem with the automatically generated array arguments. The following constructor takes any number of ints that come in array form: import std.stdio; struct S { int[] a; this(int[] args...) { a = args; } void foo() { writeln(a); } } void main() { S[] a; foreach (i; 0 .. 2) { a ~= S(i, i, i); // <-- WARNING temporary arrayThis is a known problem, I have put it in Bugzilla since lot of time and it seems there are various persons that agree with me and you on it. So hopefully this source of bugs will be removed, allocating that data on the heap. Bye, bearophile
Apr 18 2013
On 04/18/2013 02:06 PM, Brad Anderson wrote:Is this supposed to be allowed: ubyte[] a; ubyte[16] b; a = b; assert(a.ptr == b.ptr); Because if so that makes it terribly easy to do a bug like this (as I just saw in IRC): struct A { ubyte[] a; this(ubyte c) { ubyte[16] b; b[] = c; this.a = b; // a now points at an immediately invalid static array } }There is a similar problem with the automatically generated array arguments. The following constructor takes any number of ints that come in array form: import std.stdio; struct S { int[] a; this(int[] args...) { a = args; } void foo() { writeln(a); } } void main() { S[] a; foreach (i; 0 .. 2) { a ~= S(i, i, i); // <-- WARNING temporary array } foreach (e; a) { e.foo(); } } The program prints the following because the temporary arrays that are generated when calling the constructors are long gone: [1, 1, 1] [1, 1, 1] The programmer *may have* ;) expected the following output: [1, 1, 1] [2, 2, 2] Ali
Apr 18 2013
There is a similar problem with the automatically generated array arguments. The following constructor takes any number of ints that come in array form: import std.stdio; struct S { int[] a; this(int[] args...) { a = args; } void foo() { writeln(a); } } void main() { S[] a; foreach (i; 0 .. 2) { a ~= S(i, i, i); // <-- WARNING temporary array } foreach (e; a) { e.foo(); } } The program prints the following because the temporary arrays that are generated when calling the constructors are long gone: [1, 1, 1] [1, 1, 1] The programmer *may have* ;) expected the following output: [1, 1, 1] [2, 2, 2]There is no guarantee that the incoming array from a variadic function is heap-based. But an interesting way to deal with it is that you can overload with an explicit slice parameter, and the variadic version will ONLY bind to a variadic call. For example, in dcollections' ArrayList I have two constructors: /** * Create an array list based on a number of elements. */ this(V[] elems...) { _array = elems.dup; } /** * Use an array as the backing storage. This does not duplicate the * array. Use new ArrayList(storage.dup) to make a distinct copy. Beware * of using a stack-based array when using this constructor! */ this(V[] storage) { _array = storage; } The first version binds only to cases where the parameter is not *explicitly* a slice, so I am guaranteed that the array is allocated on the stack! The second binds to slices, and I assume from my comment, fixed-sized arrays (been a while since I looked at that code). And the correct expectation for your code should be: [0, 0, 0] [1, 1, 1] :) -Steve
Apr 18 2013
Steven Schveighoffer:There is no guarantee that the incoming array from a variadic function is heap-based. But an interesting way to deal with it is that you can overload with an explicit slice parameter, and the variadic version will ONLY bind to a variadic call. For example, in dcollections' ArrayList I have two constructors: /** * Create an array list based on a number of elements. */ this(V[] elems...) { _array = elems.dup; } /** * Use an array as the backing storage. This does not duplicate the * array. Use new ArrayList(storage.dup) to make a distinct copy. Beware * of using a stack-based array when using this constructor! */ this(V[] storage) { _array = storage; }To avoid those bugs I have suggested the simpler possible thing: (V[] elems...) to dup the data on the heap every time. In theory if you write "(scope V[] elems...)" it will be free to not dup the data, avoiding the heap allocation and the associated little performance loss. In practice as you know "scope" is not yet implemented. D2 language is not close to being fully implemented. Bye, bearophile
Apr 18 2013
On Thu, 18 Apr 2013 18:08:48 -0400, bearophile <bearophileHUGS lycos.com> wrote:To avoid those bugs I have suggested the simpler possible thing: (V[] elems...) to dup the data on the heap every time. In theory if you write "(scope V[] elems...)" it will be free to not dup the data, avoiding the heap allocation and the associated little performance loss. In practice as you know "scope" is not yet implemented. D2 language is not close to being fully implemented.I am OK with this as long as it is done AFTER scope works (and I would update my calls appropriately). But in my specific case, I still want the second overload, because the idea is ArrayList uses that slice as its actual storage. -Steve
Apr 19 2013
On 04/18/2013 02:54 PM, Steven Schveighoffer wrote:Interesting. Personally, I would not even bother with the second one and expect the caller to simply put square brackets around the arguments: import std.stdio; struct S { int[] a; this(int[] args) // <-- now requires a slice { a = args; } void foo() { writeln(a); } } void main() { S[] a; foreach (i; 0 .. 2) { a ~= S([i, i, i]); // <-- minor inconvenience } foreach (e; a) { e.foo(); } } It is now safe, right?The program prints the following because the temporary arrays that are generated when calling the constructors are long gone: [1, 1, 1] [1, 1, 1] The programmer *may have* ;) expected the following output: [1, 1, 1] [2, 2, 2]There is no guarantee that the incoming array from a variadic function is heap-based. But an interesting way to deal with it is that you can overload with an explicit slice parameter, and the variadic version will ONLY bind to a variadic call.And the correct expectation for your code should be: [0, 0, 0] [1, 1, 1] :) -SteveWow! I made an off-by-6 error there. :) Ali
Apr 18 2013
On Thu, 18 Apr 2013 18:13:14 -0400, Ali =C3=87ehreli <acehreli yahoo.com=wrote:Interesting. Personally, I would not even bother with the second one and expect the==caller to simply put square brackets around the arguments:[snip]It is now safe, right?Yes, for this case it is OK. But I want a consistent interface in dcollections. Other containers do not use arrays as storage, so the bracketed version for something like a TreeSet would be a complete waste= (and in fact, only the variadic version exists there). The special case= for ArrayList is the slice version, where it actually takes on that slic= e as its internal storage. ArrayList is meant to bridge the gap between D= slices and dcollections. It's range type is a D slice as well. -Steve
Apr 19 2013
On Thu, Apr 18, 2013 at 02:43:54PM -0700, Ali Çehreli wrote:On 04/18/2013 02:06 PM, Brad Anderson wrote:[...] Yeah I got bitten by this before. Took me several days to find the problem, 'cos it was nested deep inside a complex data structure, and at a glance it doesn't *look* wrong. I'm all for making this system at the very least, if not outright compile error. Storing a persistent reference to a stack-allocated object is outright wrong... in the case of variadic array args, if the compiler can't prove that args will *always* be a dynamic array, then any attempt to save a reference to it should be rejected outright IMO. T -- It is impossible to make anything foolproof because fools are so ingenious. -- SammyIs this supposed to be allowed: ubyte[] a; ubyte[16] b; a = b; assert(a.ptr == b.ptr); Because if so that makes it terribly easy to do a bug like this (as I just saw in IRC): struct A { ubyte[] a; this(ubyte c) { ubyte[16] b; b[] = c; this.a = b; // a now points at an immediately invalid static array } }There is a similar problem with the automatically generated array arguments. The following constructor takes any number of ints that come in array form: import std.stdio; struct S { int[] a; this(int[] args...) { a = args; } void foo() { writeln(a); } }
Apr 18 2013
On Thu, 18 Apr 2013 19:15:06 -0400, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:I'm all for making this system at the very least, if not outright compile error. Storing a persistent reference to a stack-allocated object is outright wrong... in the case of variadic array args, if the compiler can't prove that args will *always* be a dynamic array, then any attempt to save a reference to it should be rejected outright IMO.I like bearophile's solution, we increasingly need scope to work for D to be easy to avoid mistakes. -Steve
Apr 19 2013
Steven Schveighoffer:we increasingly need scope to work for D to be easy to avoid mistakes.But I don't see any plan/implementation timeframe for "scope" :-( I don't know why. I'd like to know Hara opinion on this. Bye, bearophile
Apr 19 2013
Steven Schveighoffer:we increasingly need scope to work for D to be easy to avoid mistakes.I have added a note about scope: http://d.puremagic.com/issues/show_bug.cgi?id=5212 Bye, bearophile
Apr 19 2013
Brad Anderson:Is this supposed to be allowed: ubyte[] a; ubyte[16] b; a = b; assert(a.ptr == b.ptr);Yes, this is supposed to be allowed with the current design of D. But with the latest dmd 2.063alpha that code doesn't work, see below. The Rust language removes this source of bugs because it manages accurately the memory zones. D will probably improve its error detection capabilities for such simple cases, but I think it will not solve this problem in general, unless it improves its type system, similarly to Rust.import std.digest.md; import std.stdio; struct Hash { ubyte[] hash1; ubyte[] hash2; this (string str) { auto md5 = new MD5Digest(); this.hash1 = md5.digest(str); this.hash2 = digest!MD5(str); } }; void main() { auto h = Hash("hello world!"); writeln(toHexString(h.hash1)); writeln(toHexString(h.hash2)); } It's not obvious at all what the problem is.Now that code gives a warning: temp.d(11): Warning: explicit slice assignment this.hash2 = (digest(str))[] is better than this.hash2 = digest(str) (This is the result of a small battle I have started years ago that is now half work, thanks to the work of Hara implementing my enhancement request. I am glad to see our time was not wasted.) To remove that warning you have to write: this.hash1 = md5.digest(str); this.hash2 = digest!MD5(str)[]; Now it's visible that for hash2 you are slicing something that's probably a fixed-sized array. This gives you a significant help in understanding what's going on. Bye, bearophile
Apr 18 2013
On Thursday, 18 April 2013 at 21:45:56 UTC, bearophile wrote:Yes, this is supposed to be allowed with the current design of D. But with the latest dmd 2.063alpha that code doesn't work, see below. [snip] Now that code gives a warning: temp.d(11): Warning: explicit slice assignment this.hash2 = (digest(str))[] is better than this.hash2 = digest(str) (This is the result of a small battle I have started years ago that is now half work, thanks to the work of Hara implementing my enhancement request. I am glad to see our time was not wasted.) To remove that warning you have to write: this.hash1 = md5.digest(str); this.hash2 = digest!MD5(str)[]; Now it's visible that for hash2 you are slicing something that's probably a fixed-sized array. This gives you a significant help in understanding what's going on.That's good to hear, Bearophile. Thanks Kenji.
Apr 18 2013