digitalmars.D - Zero-length static array spec
- Stefan Frijters (43/43) Feb 01 2015 So recently I ran into this discrepancy between the behaviour of
- ketmar (4/6) Feb 01 2015 i believe that zero-length array should not be `null`, as it's=20
- Iain Buclaw via Digitalmars-d (10/16) Feb 01 2015 Infinitely small:
- Andrei Alexandrescu (2/4) Feb 01 2015 That could get pretty big. -- Andrei
- Stefan Frijters (3/11) Feb 01 2015 Fwiw, I would also like this to be the case, as to not have to
- H. S. Teoh via Digitalmars-d (9/16) Feb 01 2015 [...]
- Iain Buclaw via Digitalmars-d (4/17) Feb 01 2015 I was going for the opposite of "multiplied". Let's ignore that
- Iain Buclaw via Digitalmars-d (11/27) Feb 01 2015 Regardless of size, a static array should always have an address on
- ketmar (4/18) Feb 01 2015 actually, `data0.ptr` can point anywhere (except 0 as null ;-), 'cause=2...
- H. S. Teoh via Digitalmars-d (7/28) Feb 01 2015 Actually, it *can* be null too, since it doesn't actually take up any
- ketmar (3/10) Feb 01 2015 then it will become non-existent too, as putting something to the place=...
- David Nadlinger (10/22) Feb 06 2015 Let's have a look at a related example:
- ketmar (6/33) Feb 06 2015 that's easy: just start assigning increasing addresses from end of ram o...
- David Nadlinger (10/19) Feb 06 2015 You mean assigning during compilation by keeping some global
- ketmar (4/14) Feb 06 2015 only for the current function. it doesn't matter if another function has...
- Daniel Murphy (7/14) Feb 06 2015 I seriously doubt the intent of that line in the spec was meant to apply...
- Iain Buclaw via Digitalmars-d (5/28) Feb 06 2015 For debugging purposes, it must have a position on the stack pointer.
- David Nadlinger (7/12) Feb 06 2015 That's not clear to me from the spec. One could argue that a
- Iain Buclaw via Digitalmars-d (7/18) Feb 06 2015 Simple, you implement it by allocating no memory. :)
- David Nadlinger (16/17) Feb 06 2015 Let me put it a different way. Imagine you have this in your
- FG (18/30) Feb 07 2015 i am convinced that a0, ..., a99 are not variables but rather *labels* a...
- Iain Buclaw via Digitalmars-d (8/51) Feb 07 2015 This is OK - gets passed as ubyte*
- FG (2/11) Feb 07 2015 Oh, I see. That is quite a nice solution. Better than compilation error.
- Iain Buclaw via Digitalmars-d (13/29) Feb 07 2015 There are no addresses for them, you can't do *anything* with them at ru...
- David Nadlinger (6/12) Feb 07 2015 What does "arr0" refer to?
- Iain Buclaw via Digitalmars-d (10/19) Feb 07 2015 A zero length static array.
- David Nadlinger (11/14) Feb 07 2015 I don't think that's a viable option. It would lead to completely
- Iain Buclaw via Digitalmars-d (10/23) Feb 07 2015 You should be able to solve this in the compiler. For instance, can
- David Nadlinger (9/26) Feb 07 2015 I just noticed that I managed to make a typo in the example. a1
- Iain Buclaw via Digitalmars-d (7/35) Feb 07 2015 To give another clue.
- Iain Buclaw via Digitalmars-d (7/35) Feb 07 2015 Though having a quick skim through the llvm class documentation,
- Brian Schott (3/6) Feb 07 2015 Fortunately it is very trivial to add a static analysis check for
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (11/45) Feb 07 2015 Why's that? Shouldn't _all_ 0 size variables compare equal? I'd
- kinke (27/37) Feb 26 2015 +1.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (2/41) Feb 27 2015
- Steven Schveighoffer (7/39) Feb 02 2015 The lynch pin here is that "it's useful as the last member of a variable...
- David Nadlinger (25/29) Feb 06 2015 This use case (i.e. as part of a bigger aggregate) already works
- Steven Schveighoffer (3/12) Feb 06 2015 What about a closure? Isn't that a struct allocated on the heap?
So recently I ran into this discrepancy between the behaviour of dmd (and gdc) and ldc2: void test(int[] data) in { assert(data, "data must be non-null."); } body { } void main() { import std.stdio; int[1] data1; writeln(data1); // [0] test(data1); // Passes assert(data1.ptr !is null); int[0] data0; writeln(data0); // [] test(data0); // Passes with dmd and gdc, fails with ldc2 (2.066.1) assert(data0.ptr !is null); // Passes with dmd and gdc, fails with ldc2 } I reported this as an issue at https://github.com/ldc-developers/ldc/issues/831 and was asked to check for a more definite answer. So, in light of recent developments of trying to tighten up the D spec, does anyone have any insight what the correct behaviour should be, and can it be locked down in the spec? Currently the D spec says [1]: --- Static Arrays int[3] s; These are analogous to C arrays. Static arrays are distinguished by having a length fixed at compile time. The total size of a static array cannot exceed 16Mb. A dynamic array should be used instead for such large arrays. A static array with a dimension of 0 is allowed, but no space is allocated for it. It's useful as the last member of a variable length struct, or as the degenerate case of a template expansion. Static arrays are value types. Unlike in C and D version 1, static arrays are passed to functions by value. Static arrays can also be returned by functions. --- It does not seem to say whether a zero-length array should have a valid address or not. Thoughts? [1] http://dlang.org/arrays.html
Feb 01 2015
On Sun, 01 Feb 2015 14:42:31 +0000, Stefan Frijters wrote:It does not seem to say whether a zero-length array should have a valid address or not.i believe that zero-length array should not be `null`, as it's=20 "infinitely small", yet not "non-existent". at least this is what my=20 logic tells me.=
Feb 01 2015
On 1 February 2015 at 14:54, ketmar via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Sun, 01 Feb 2015 14:42:31 +0000, Stefan Frijters wrote:Infinitely small: Smaller than the smallest thing ever and then some more. Much smaller than that in fact, really amazingly insignificant, a totally unimpressive size, real 'wow, that's small', time. Infinitely small is just so small that by comparison, smallness itself looks really humongous. Miniscule divided by meager divided by staggeringly infinitesimal is the sort of concept we're trying to get across here. :-)It does not seem to say whether a zero-length array should have a valid address or not.i believe that zero-length array should not be `null`, as it's "infinitely small", yet not "non-existent". at least this is what my logic tells me.
Feb 01 2015
On 2/1/15 7:11 AM, Iain Buclaw via Digitalmars-d wrote:Miniscule divided by meager divided by staggeringly infinitesimal is the sort of concept we're trying to get across here.That could get pretty big. -- Andrei
Feb 01 2015
On Sunday, 1 February 2015 at 14:54:37 UTC, ketmar wrote:On Sun, 01 Feb 2015 14:42:31 +0000, Stefan Frijters wrote:Fwiw, I would also like this to be the case, as to not have to handle any special cases when using int[N] templates and such.It does not seem to say whether a zero-length array should have a valid address or not.i believe that zero-length array should not be `null`, as it's "infinitely small", yet not "non-existent". at least this is what my logic tells me.
Feb 01 2015
On Sun, Feb 01, 2015 at 03:11:07PM +0000, Iain Buclaw via Digitalmars-d wrote: [...]Infinitely small: Smaller than the smallest thing ever and then some more. Much smaller than that in fact, really amazingly insignificant, a totally unimpressive size, real 'wow, that's small', time. Infinitely small is just so small that by comparison, smallness itself looks really humongous. Miniscule divided by meager divided by staggeringly infinitesimal is the sort of concept we're trying to get across here.[...] Wait wait wait... Miniscule *divided* by meager divided by staggeringly infinitesimal?! Wouldn't that be a non-standard number larger than any finite number? T -- Computerese Irregular Verb Conjugation: I have preferences. You have biases. He/She has prejudices. -- Gene Wirchenko
Feb 01 2015
On 1 February 2015 at 15:46, H. S. Teoh via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Sun, Feb 01, 2015 at 03:11:07PM +0000, Iain Buclaw via Digitalmars-d wrote: [...]I was going for the opposite of "multiplied". Let's ignore that decimal semantics exist for a moment. :)Infinitely small: Smaller than the smallest thing ever and then some more. Much smaller than that in fact, really amazingly insignificant, a totally unimpressive size, real 'wow, that's small', time. Infinitely small is just so small that by comparison, smallness itself looks really humongous. Miniscule divided by meager divided by staggeringly infinitesimal is the sort of concept we're trying to get across here.[...] Wait wait wait... Miniscule *divided* by meager divided by staggeringly infinitesimal?! Wouldn't that be a non-standard number larger than any finite number?
Feb 01 2015
On 1 February 2015 at 14:42, Stefan Frijters via Digitalmars-d <digitalmars-d puremagic.com> wrote:Static Arrays int[3] s; These are analogous to C arrays. Static arrays are distinguished by having a length fixed at compile time. The total size of a static array cannot exceed 16Mb. A dynamic array should be used instead for such large arrays. A static array with a dimension of 0 is allowed, but no space is allocated for it. It's useful as the last member of a variable length struct, or as the degenerate case of a template expansion. Static arrays are value types. Unlike in C and D version 1, static arrays are passed to functions by value. Static arrays can also be returned by functions. --- It does not seem to say whether a zero-length array should have a valid address or not. Thoughts?Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. Consider: int[0] data0; int[1] data1; Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr == data1.ptr should not succeed either.
Feb 01 2015
On Sun, 01 Feb 2015 15:34:39 +0000, Iain Buclaw via Digitalmars-d wrote:Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. =20 You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. =20 Consider: =20 int[0] data0; int[1] data1; =20 =20 Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr =3D=3D data1.ptr should not succeed either.actually, `data0.ptr` can point anywhere (except 0 as null ;-), 'cause=20 it's so small that it takes no room at all, so it doesn't matter where it=20 will be placed. ;-)=
Feb 01 2015
On Sun, Feb 01, 2015 at 03:46:15PM +0000, ketmar via Digitalmars-d wrote:On Sun, 01 Feb 2015 15:34:39 +0000, Iain Buclaw via Digitalmars-d wrote:Actually, it *can* be null too, since it doesn't actually take up any space, so it would fit perfectly fine in nullspace (non-existent space) as well. :-P T -- IBM = I'll Buy Microsoft!Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. Consider: int[0] data0; int[1] data1; Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr == data1.ptr should not succeed either.actually, `data0.ptr` can point anywhere (except 0 as null ;-), 'cause it's so small that it takes no room at all, so it doesn't matter where it will be placed. ;-)
Feb 01 2015
On Sun, 01 Feb 2015 07:50:08 -0800, H. S. Teoh via Digitalmars-d wrote:then it will become non-existent too, as putting something to the place=20 that doesn't exist turns that to non-existent. ;-)=actually, `data0.ptr` can point anywhere (except 0 as null ;-), 'cause it's so small that it takes no room at all, so it doesn't matter where it will be placed. ;-)=20 Actually, it *can* be null too, since it doesn't actually take up any space, so it would fit perfectly fine in nullspace (non-existent space) as well. :-P
Feb 01 2015
On Sunday, 1 February 2015 at 15:34:48 UTC, Iain Buclaw wrote:Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. Consider: int[0] data0; int[1] data1; Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr == data1.ptr should not succeed either.Let's have a look at a related example: int[0] data0; int[0] data1; assert(data0.ptr != data1.ptr); // ??? If you want this assert to succeed, how do you ensure that the addresses are different without allocating at least one byte of stack space (which currently seems to be prohibited by the "does not take up space" clause). David
Feb 06 2015
On Fri, 06 Feb 2015 16:33:49 +0000, David Nadlinger wrote:On Sunday, 1 February 2015 at 15:34:48 UTC, Iain Buclaw wrote:that's easy: just start assigning increasing addresses from end of ram or=20 start of ram for each "infinitely small" local. as passing it's address=20 is an invalid operation, the only valid thing one can do is compare it=20 with null or some other address, and pass the result of comparison, so=20 it's ok.=Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. Consider: int[0] data0; int[1] data1; Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr =3D=3D data1.ptr should not succeed either.=20 Let's have a look at a related example: =20 int[0] data0; int[0] data1; =20 assert(data0.ptr !=3D data1.ptr); // ??? =20 If you want this assert to succeed, how do you ensure that the addresses are different without allocating at least one byte of stack space (which currently seems to be prohibited by the "does not take up space" clause).
Feb 06 2015
On Friday, 6 February 2015 at 16:57:11 UTC, ketmar wrote:that's easy: just start assigning increasing addresses from end of ram or start of ram for each "infinitely small" local. as passing it's address is an invalid operation, the only valid thing one can do is compare it with null or some other address, and pass the result of comparison, so it's ok.You mean assigning during compilation by keeping some global state around somewhere? I think this would cause all kinds of issues with separate compilation and/or reproducibility of builds. Using some kind of unique hash identifying the variable declaration might just work, but before starting to forge addresses and hoping that they don't overlap another allocation (on a 32 bit system) or otherwise confuse users, I'd rather be sure that there is value in this behavior in the first place. David
Feb 06 2015
On Fri, 06 Feb 2015 17:37:36 +0000, David Nadlinger wrote:On Friday, 6 February 2015 at 16:57:11 UTC, ketmar wrote:only for the current function. it doesn't matter if another function has=20 the same addresses assigned, as it is allowed to reuse stack space. maybe=20 even only for the current code block is enough.=that's easy: just start assigning increasing addresses from end of ram or start of ram for each "infinitely small" local. as passing it's address is an invalid operation, the only valid thing one can do is compare it with null or some other address, and pass the result of comparison, so it's ok.=20 You mean assigning during compilation by keeping some global state around somewhere? I think this would cause all kinds of issues with separate compilation and/or reproducibility of builds.
Feb 06 2015
"David Nadlinger" wrote in message news:actxhygoikohznovzbcl forum.dlang.org...Let's have a look at a related example: int[0] data0; int[0] data1; assert(data0.ptr != data1.ptr); // ??? If you want this assert to succeed, how do you ensure that the addresses are different without allocating at least one byte of stack space (which currently seems to be prohibited by the "does not take up space" clause).I seriously doubt the intent of that line in the spec was meant to apply to stack-allocated zero length static arrays. I think we should just change it so that we are allowed to allocate some non-zero amount of stack space for the array and give it a valid pointer. Layout matters when used in a struct, not so much when it's on the stack.
Feb 06 2015
On 6 February 2015 at 16:33, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Sunday, 1 February 2015 at 15:34:48 UTC, Iain Buclaw wrote:For debugging purposes, it must have a position on the stack pointer. For semantic purposes, comparing it as being equal to another value must be false.Regardless of size, a static array should always have an address on the stack. Of course, dereferencing said address is undefined. You can also consider it a require that although a zero-length static array may have an address, it doesn't take up any space either. Consider: int[0] data0; int[1] data1; Here, you could expect both data0 and data1 to have the same .ptr address, but data0.ptr == data1.ptr should not succeed either.Let's have a look at a related example: int[0] data0; int[0] data1; assert(data0.ptr != data1.ptr); // ??? If you want this assert to succeed, how do you ensure that the addresses are different without allocating at least one byte of stack space (which currently seems to be prohibited by the "does not take up space" clause).
Feb 06 2015
On Friday, 6 February 2015 at 19:10:01 UTC, Iain Buclaw wrote:For debugging purposes, it must have a position on the stack pointer.This sentence does not parse for me. "on the stack *pointer*"?For semantic purposes, comparing it as being equal to another value must be false.That's not clear to me from the spec. One could argue that a zero-byte entity can by definition not have an identity. In any case, my question was how to implement that behavior without allocating memory, which the spec currently prohibits. David
Feb 06 2015
On 6 February 2015 at 22:16, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Friday, 6 February 2015 at 19:10:01 UTC, Iain Buclaw wrote:Simple, you implement it by allocating no memory. :) I'll have to see what goes on, I can't imagine that llvm won't provide some way to do this. GDC's behaviour is incident in the backend rather than intent. Iain.For debugging purposes, it must have a position on the stack pointer.This sentence does not parse for me. "on the stack *pointer*"?For semantic purposes, comparing it as being equal to another value must be false.That's not clear to me from the spec. One could argue that a zero-byte entity can by definition not have an identity. In any case, my question was how to implement that behavior without allocating memory, which the spec currently prohibits. David
Feb 06 2015
On Friday, 6 February 2015 at 23:37:30 UTC, Iain Buclaw wrote:Simple, you implement it by allocating no memory. :)Let me put it a different way. Imagine you have this in your program: --- void foo() { int[0] a0; int[0] a1; ... int[0] a99; // Do something with them. } --- How do you choose the addresses for a0 through a99 so that they are distinct, but you don't end up allocating 100 bytes of stack memory? David
Feb 06 2015
On 2015-02-07 at 00:42, David Nadlinger wrote:Imagine you have this in your program: --- void foo() { int[0] a0; int[0] a1; ... int[0] a99; // Do something with them. } --- How do you choose the addresses for a0 through a99 so that they are distinct, but you don't end up allocating 100 bytes of stack memory?Forgive my ignorance and perhaps not seeing the whole picture, but when I read this:A static array with a dimension of 0 is allowed, but no space is allocated for it. It's useful as the last member of a variable length struct...i am convinced that a0, ..., a99 are not variables but rather *labels* and should point to *the same address* of whatever could be put in that place (with 1-byte alignment). I do not understand why would they ever point to different places. Wouldn't that make them useless when used in structs like the following one (or to access stack, like above)? struct Packet { int a, b, c, length; ubyte[0] data; ubyte[0] payload; // just a superfluous alias to data } unittest { Packet p; assert(p.sizeof == 16); assert(&p + 1 == cast(void*) &p.data); assert(&p + 1 == cast(void*) &p.payload); } As for passing it around, it doesn't make sense, it is like passing an argument of type void, so shouldn't be allowed. Only a pointer to a zero-length array or a specified element would be fine: foo(&p.data) // fine, ubyte* bar(p.data[i]) // fine, ubyte (or memory violation) xxx(p.data) // ERROR, makes no sense, shouldn't compile
Feb 07 2015
On 7 February 2015 at 10:56, FG via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 2015-02-07 at 00:42, David Nadlinger wrote:This is OK - gets passed as ubyte*Imagine you have this in your program: --- void foo() { int[0] a0; int[0] a1; ... int[0] a99; // Do something with them. } --- How do you choose the addresses for a0 through a99 so that they are distinct, but you don't end up allocating 100 bytes of stack memory?Forgive my ignorance and perhaps not seeing the whole picture, but when I read this:A static array with a dimension of 0 is allowed, but no space is allocated for it. It's useful as the last member of a variable length struct...i am convinced that a0, ..., a99 are not variables but rather *labels* and should point to *the same address* of whatever could be put in that place (with 1-byte alignment). I do not understand why would they ever point to different places. Wouldn't that make them useless when used in structs like the following one (or to access stack, like above)? struct Packet { int a, b, c, length; ubyte[0] data; ubyte[0] payload; // just a superfluous alias to data } unittest { Packet p; assert(p.sizeof == 16); assert(&p + 1 == cast(void*) &p.data); assert(&p + 1 == cast(void*) &p.payload); } As for passing it around, it doesn't make sense, it is like passing an argument of type void, so shouldn't be allowed. Only a pointer to a zero-length array or a specified element would be fine: foo(&p.data) // fine, ubyte*bar(p.data[i]) // fine, ubyte (or memory violation)This is OK - gets passed as ubyte - though will throw arrayBounds error unless -noboundschecks.xxx(p.data) // ERROR, makes no sense, shouldn't compileThis is OK - gets passed as ubyte[] - the dynamic array will have a length of '0' and the ptr to &p.data. Iain.
Feb 07 2015
On 2015-02-07 at 13:21, Iain Buclaw via Digitalmars-d wrote:Oh, I see. That is quite a nice solution. Better than compilation error.foo(&p.data) // fine, ubyte*This is OK - gets passed as ubyte*bar(p.data[i]) // fine, ubyte (or memory violation)This is OK - gets passed as ubyte - though will throw arrayBounds error unless -noboundschecks.xxx(p.data) // ERROR, makes no sense, shouldn't compileThis is OK - gets passed as ubyte[] - the dynamic array will have a length of '0' and the ptr to &p.data.
Feb 07 2015
On 6 February 2015 at 23:42, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Friday, 6 February 2015 at 23:37:30 UTC, Iain Buclaw wrote:There are no addresses for them, you can't do *anything* with them at runtime. Some cod scenarios: 1. Comparisons of == fold into 'false' (unless comparing to self) 2. Comparisons of != fold into 'true' (unless comparing to self) 3. Conversion to dynamic arrays is { .length=0, .ptr=&arr0 } You cannot assign memory/address to them directly, but you can assign it indirectly. This 'indirect assignment' can only really work if it were the last member of a struct. Example usage - http://goo.gl/uAbxKN - something like a pointer-based dynamic array. Labels probably do best describe their exact behaviour. Iain.Simple, you implement it by allocating no memory. :)Let me put it a different way. Imagine you have this in your program: --- void foo() { int[0] a0; int[0] a1; ... int[0] a99; // Do something with them. } --- How do you choose the addresses for a0 through a99 so that they are distinct, but you don't end up allocating 100 bytes of stack memory? David
Feb 07 2015
On Saturday, 7 February 2015 at 12:10:45 UTC, Iain Buclaw wrote:Some cod scenarios: 1. Comparisons of == fold into 'false' (unless comparing to self) 2. Comparisons of != fold into 'true' (unless comparing to self) 3. Conversion to dynamic arrays is { .length=0, .ptr=&arr0 }What does "arr0" refer to? And assuming that a0.ptr and a1.ptr would end up being the same in your proposal, do you really want to have a situation where a0.ptr == a1.ptr && a0.length == a1.length but a0 != a1? David
Feb 07 2015
On 7 Feb 2015 12:50, "David Nadlinger via Digitalmars-d" < digitalmars-d puremagic.com> wrote:On Saturday, 7 February 2015 at 12:10:45 UTC, Iain Buclaw wrote:A zero length static array.Some cod scenarios: 1. Comparisons of == fold into 'false' (unless comparing to self) 2. Comparisons of != fold into 'true' (unless comparing to self) 3. Conversion to dynamic arrays is { .length=0, .ptr=&arr0 }What does "arr0" refer to?And assuming that a0.ptr and a1.ptr would end up being the same in yourproposal, do you really want to have a situation where a0.ptr == a1.ptr && a0.length == a1.length but a0 != a1?Davida0.ptr == a1.ptr // false, enforced by compiler. Comparison not actually emitted. a0.length == a1.length // true, both are 0 a0 != a1 // true, same effect as first. Iain.
Feb 07 2015
On Saturday, 7 February 2015 at 15:04:52 UTC, Iain Buclaw wrote:a0.ptr == a1.ptr // false, enforced by compiler. Comparison not actually emitted.I don't think that's a viable option. It would lead to completely unintuitive behavior like the following: --- bool isEqual(T)(T a, T b) { return a == b; } int[0] a0; int[1] a1; assert(a0.ptr != a1.ptr); assert(isEqual(a0.ptr, a1.ptr)); --- David
Feb 07 2015
On 7 February 2015 at 22:45, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Saturday, 7 February 2015 at 15:04:52 UTC, Iain Buclaw wrote:You should be able to solve this in the compiler. For instance, can you define an array type to having a non-fixed size? Check if you can do something in terms of: --- int[1] a1; int[0:18446744073709551615] a0; // size_t.max --- Iain.a0.ptr == a1.ptr // false, enforced by compiler. Comparison not actually emitted.I don't think that's a viable option. It would lead to completely unintuitive behavior like the following: --- bool isEqual(T)(T a, T b) { return a == b; } int[0] a0; int[1] a1; assert(a0.ptr != a1.ptr); assert(isEqual(a0.ptr, a1.ptr)); ---
Feb 07 2015
On Sunday, 8 February 2015 at 00:51:21 UTC, Iain Buclaw wrote:I just noticed that I managed to make a typo in the example. a1 should have also been of type int[0]. In any case, the implementation on the different compiler backends is not at all what I'm talking about at this point. You proposed special comparison semantics, and I highlighted what seems to be a deal-breaker to me. All this is on the language spec level and has nothing to do with the backend implementation. DavidI don't think that's a viable option. It would lead to completely unintuitive behavior like the following: --- bool isEqual(T)(T a, T b) { return a == b; } int[0] a0; int[1] a1; assert(a0.ptr != a1.ptr); assert(isEqual(a0.ptr, a1.ptr)); ---You should be able to solve this in the compiler. For instance, can you define an array type to having a non-fixed size?
Feb 07 2015
On 8 February 2015 at 00:51, Iain Buclaw <ibuclaw gdcproject.org> wrote:On 7 February 2015 at 22:45, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:To give another clue. int[1] a1; Is an array type with an index type whose range is 0..0 int[2] a2; Is an array type with an index type whose range is 0..1 int[0] a0; Is an array type with an index type whose range is 0..-1 The hint here is the range is always 0 .. size - 1 Iain.On Saturday, 7 February 2015 at 15:04:52 UTC, Iain Buclaw wrote:You should be able to solve this in the compiler. For instance, can you define an array type to having a non-fixed size? Check if you can do something in terms of: --- int[1] a1; int[0:18446744073709551615] a0; // size_t.max ---a0.ptr == a1.ptr // false, enforced by compiler. Comparison not actually emitted.I don't think that's a viable option. It would lead to completely unintuitive behavior like the following: --- bool isEqual(T)(T a, T b) { return a == b; } int[0] a0; int[1] a1; assert(a0.ptr != a1.ptr); assert(isEqual(a0.ptr, a1.ptr)); ---
Feb 07 2015
On 8 February 2015 at 00:51, Iain Buclaw <ibuclaw gdcproject.org> wrote:On 7 February 2015 at 22:45, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:Though having a quick skim through the llvm class documentation, doesn't seem they have a notion of this. So maybe your best bet is to delay pushing zero-length arrays? I will admit it is an odd special case. And definitely in the land of WAT if someone uses this in production code. Iain.On Saturday, 7 February 2015 at 15:04:52 UTC, Iain Buclaw wrote:You should be able to solve this in the compiler. For instance, can you define an array type to having a non-fixed size? Check if you can do something in terms of: --- int[1] a1; int[0:18446744073709551615] a0; // size_t.max ---a0.ptr == a1.ptr // false, enforced by compiler. Comparison not actually emitted.I don't think that's a viable option. It would lead to completely unintuitive behavior like the following: --- bool isEqual(T)(T a, T b) { return a == b; } int[0] a0; int[1] a1; assert(a0.ptr != a1.ptr); assert(isEqual(a0.ptr, a1.ptr)); ---
Feb 07 2015
On Sunday, 8 February 2015 at 01:17:02 UTC, Iain Buclaw wrote:I will admit it is an odd special case. And definitely in the land of WAT if someone uses this in production code.Fortunately it is very trivial to add a static analysis check for this.
Feb 07 2015
On Saturday, 7 February 2015 at 12:10:45 UTC, Iain Buclaw wrote:On 6 February 2015 at 23:42, David Nadlinger via Digitalmars-d <digitalmars-d puremagic.com> wrote:Why's that? Shouldn't _all_ 0 size variables compare equal? I'd say that follows from the definition: array1 == array2 <=> all corresponding elements of both arrays are equal <=> no corresponding elements of both arrays are different For the last one, it's obvious that this applies to empty arrays: they don't even contain elements that could compare unequal. Or maybe you're only talking about the addresses here?On Friday, 6 February 2015 at 23:37:30 UTC, Iain Buclaw wrote:There are no addresses for them, you can't do *anything* with them at runtime. Some cod scenarios: 1. Comparisons of == fold into 'false' (unless comparing to self) 2. Comparisons of != fold into 'true' (unless comparing to self)Simple, you implement it by allocating no memory. :)Let me put it a different way. Imagine you have this in your program: --- void foo() { int[0] a0; int[0] a1; ... int[0] a99; // Do something with them. } --- How do you choose the addresses for a0 through a99 so that they are distinct, but you don't end up allocating 100 bytes of stack memory? David
Feb 07 2015
Why's that? Shouldn't _all_ 0 size variables compare equal? I'd say that follows from the definition: array1 == array2 <=> all corresponding elements of both arrays are equal <=> no corresponding elements of both arrays are different For the last one, it's obvious that this applies to empty arrays: they don't even contain elements that could compare unequal.+1. My proposal: let the front-end handle all 0-sized types in a special way. Note that this definition includes: * zero-length static arrays * static arrays of 0-sized elements * structs with no or only 0-sized elements * void By definition, instances of these types don't exist physically, so no memory should be allocated. Then, at least to me it feels natural to disallow taking their addresses, or, if that's too strict for degenerate templates, to define it to be null (the address shouldn't really matter as long as the template later reads T.sizeof=0 bytes from that address and doesn't use it to compute offsets). I don't think that hacky last-element-of-variable-length-struct use-case is worth the pain, there's got to be a cleaner solution for that. Instead, variables of these types represent purely implicit symbols. 0-sized static arrays have a compile-time length, their .ptr is implicitly defined as null. 0-sized static arrays of corresponding element type are equal as long as their lengths match, just like 0-sized structs of corresponding type are equal. The front-end would need to fold all usages of these 0-sized variables. It would omit all declared function parameters/arguments, return values (=> void) and all other variables of size 0 for the different back-ends.
Feb 26 2015
On Thursday, 26 February 2015 at 23:17:29 UTC, kinke wrote:D has `.offsetof`.Why's that? Shouldn't _all_ 0 size variables compare equal? I'd say that follows from the definition: array1 == array2 <=> all corresponding elements of both arrays are equal <=> no corresponding elements of both arrays are different For the last one, it's obvious that this applies to empty arrays: they don't even contain elements that could compare unequal.+1. My proposal: let the front-end handle all 0-sized types in a special way. Note that this definition includes: * zero-length static arrays * static arrays of 0-sized elements * structs with no or only 0-sized elements * void By definition, instances of these types don't exist physically, so no memory should be allocated. Then, at least to me it feels natural to disallow taking their addresses, or, if that's too strict for degenerate templates, to define it to be null (the address shouldn't really matter as long as the template later reads T.sizeof=0 bytes from that address and doesn't use it to compute offsets). I don't think that hacky last-element-of-variable-length-struct use-case is worth the pain, there's got to be a cleaner solution for that.Instead, variables of these types represent purely implicit symbols. 0-sized static arrays have a compile-time length, their .ptr is implicitly defined as null. 0-sized static arrays of corresponding element type are equal as long as their lengths match, just like 0-sized structs of corresponding type are equal. The front-end would need to fold all usages of these 0-sized variables. It would omit all declared function parameters/arguments, return values (=> void) and all other variables of size 0 for the different back-ends.
Feb 27 2015
On 2/1/15 9:42 AM, Stefan Frijters wrote:So recently I ran into this discrepancy between the behaviour of dmd (and gdc) and ldc2: void test(int[] data) in { assert(data, "data must be non-null."); }Note, this will soon fail to compile. use assert(data.ptr) instead.body { } void main() { import std.stdio; int[1] data1; writeln(data1); // [0] test(data1); // Passes assert(data1.ptr !is null); int[0] data0; writeln(data0); // [] test(data0); // Passes with dmd and gdc, fails with ldc2 (2.066.1) assert(data0.ptr !is null); // Passes with dmd and gdc, fails with ldc2 } I reported this as an issue at https://github.com/ldc-developers/ldc/issues/831 and was asked to check for a more definite answer. So, in light of recent developments of trying to tighten up the D spec, does anyone have any insight what the correct behaviour should be, and can it be locked down in the spec? Currently the D spec says [1]: --- Static Arrays int[3] s; These are analogous to C arrays. Static arrays are distinguished by having a length fixed at compile time. The total size of a static array cannot exceed 16Mb. A dynamic array should be used instead for such large arrays. A static array with a dimension of 0 is allowed, but no space is allocated for it. It's useful as the last member of a variable length struct, or as the degenerate case of a template expansion.The lynch pin here is that "it's useful as the last member of a variable length struct." If it's given the address of 0, then it's no longer useful there, so it should take an address of where it is defined. I'd say this is definitely a bug (and the docs should be clearer on this). -Steve
Feb 02 2015
On Monday, 2 February 2015 at 14:32:16 UTC, Steven Schveighoffer wrote:The lynch pin here is that "it's useful as the last member of a variable length struct." If it's given the address of 0, then it's no longer useful there, so it should take an address of where it is defined.This use case (i.e. as part of a bigger aggregate) already works in LDC. The issue is specific to zero-size stack allocations. Last time I checked it wasn't possible to do this with LLVM in a sensible way without allocating a non-zero amount of memory. In contrast to the current behavior, this would actually violate the spec as it is worded now. We can certainly discuss if and how to amend the spec to cater for certain use cases. However, this might be more tricky than it seems at first. For instance, since a pointer to a zero-length static array can never be legally dereferenced, we could just as well always make .ptr return 0xcafebabe for all cases where the array is not part of a larger object. Clearly this seems rather absurd, but how would you want to tighten down the spec? Recall that in a C-like memory model, no meaning is attached to the numerical value of a pointer per se, and it is undefined behavior to perform arithmetic or comparisons between pointers that refer to different allocations. Formalizing anything beyond that is certainly not a task I want to tackle, and I suspect it would be a portability nightmare regarding modern compiler backends. Cheers, David
Feb 06 2015
On 2/6/15 11:15 AM, David Nadlinger wrote:On Monday, 2 February 2015 at 14:32:16 UTC, Steven Schveighoffer wrote:What about a closure? Isn't that a struct allocated on the heap? -SteveThe lynch pin here is that "it's useful as the last member of a variable length struct." If it's given the address of 0, then it's no longer useful there, so it should take an address of where it is defined.This use case (i.e. as part of a bigger aggregate) already works in LDC. The issue is specific to zero-size stack allocations. Last time I checked it wasn't possible to do this with LLVM in a sensible way without allocating a non-zero amount of memory. In contrast to the current behavior, this would actually violate the spec as it is worded now.
Feb 06 2015