www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Zero-length static array spec

reply "Stefan Frijters" <sfrijters gmail.com> writes:
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
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
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
next sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:

 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.
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. :-)
Feb 01 2015
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
prev sibling next sibling parent "Stefan Frijters" <sfrijters gmail.com> writes:
On Sunday, 1 February 2015 at 14:54:37 UTC, ketmar wrote:
 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 "infinitely small", yet not "non-existent". at least this is what my logic tells me.
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.
Feb 01 2015
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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
prev sibling parent Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 [...]
 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?
I was going for the opposite of "multiplied". Let's ignore that decimal semantics exist for a moment. :)
Feb 01 2015
prev sibling next sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
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
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 
 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. ;-)
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!
Feb 01 2015
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Sun, 01 Feb 2015 07:50:08 -0800, H. S. Teoh via Digitalmars-d wrote:

 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
then it will become non-existent too, as putting something to the place=20 that doesn't exist turns that to non-existent. ;-)=
Feb 01 2015
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
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
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
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:
 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).
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.=
Feb 06 2015
parent reply "David Nadlinger" <code klickverbot.at> writes:
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
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Fri, 06 Feb 2015 17:37:36 +0000, David Nadlinger wrote:

 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.
=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.
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.=
Feb 06 2015
prev sibling next sibling parent "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"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
prev sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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).
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.
Feb 06 2015
parent reply "David Nadlinger" <code klickverbot.at> writes:
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
parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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
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.
Feb 06 2015
parent reply "David Nadlinger" <code klickverbot.at> writes:
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
next sibling parent reply FG <home fgda.pl> writes:
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
parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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*
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 compile
This 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
parent FG <home fgda.pl> writes:
On 2015-02-07 at 13:21, Iain Buclaw via Digitalmars-d wrote:
      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 compile
This is OK - gets passed as ubyte[] - the dynamic array will have a length of '0' and the ptr to &p.data.
Oh, I see. That is quite a nice solution. Better than compilation error.
Feb 07 2015
prev sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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
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.
Feb 07 2015
next sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
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
parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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?
A zero length static array.
 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
a0.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
parent reply "David Nadlinger" <code klickverbot.at> writes:
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
next sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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)); ---
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.
Feb 07 2015
parent "David Nadlinger" <code klickverbot.at> writes:
On Sunday, 8 February 2015 at 00:51:21 UTC, Iain Buclaw wrote:
 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));
 ---
You should be able to solve this in the compiler. For instance, can you define an array type to having a non-fixed size?
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. David
Feb 07 2015
prev sibling next sibling parent Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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)); ---
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 ---
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.
Feb 07 2015
prev sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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)); ---
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 ---
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.
Feb 07 2015
parent "Brian Schott" <briancschott gmail.com> writes:
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
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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:
 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
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)
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?
Feb 07 2015
parent reply "kinke" <noone nowhere.com> writes:
 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
parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Thursday, 26 February 2015 at 23:17:29 UTC, kinke 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.
+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.
D has `.offsetof`.
 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
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply "David Nadlinger" <code klickverbot.at> writes:
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
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/6/15 11:15 AM, David Nadlinger wrote:
 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.
What about a closure? Isn't that a struct allocated on the heap? -Steve
Feb 06 2015