November 14, 2016
On Sunday, 13 November 2016 at 23:39:37 UTC, Steven Schveighoffer wrote:

> Note that he is declaring an int[10] inside the function and then returning it. The compiler must see that the int[10] will be returned, and so it reuses the pre-allocated buffer for returning as the same address to avoid copying.
>
> I would guess it's probably fine, with no dangling reference.

Thank you for your comment.

On my side there is still something mysterious. I one case:

int[] f()
{
  int[10] sa;
  foreach(int i, ref sa_i;sa){
    sa_i=i;
  }
  int[] sb=sa;
  return sb;
}

void f_test()
{
  auto sb=f();
  sb[2]=100;     // Valgrind ok
}

Valgrind does not complain.

But on the other case:

// same int[] f() function

void f_test() {
    auto sb=f();
    sb[2] = 100;
    writeln(sb[2]);
    int test[100];  // these two lines make Valgrind panicking
    writeln(sb[2]); //
}

it complains with "Conditional jump or move depends on uninitialised value(s)"


I think my two examples are creating a dangling pointer. But I would be happy to understand why Valgrind is not complaining for the first one.

Vincent


November 13, 2016
On Monday, November 14, 2016 05:53:04 Picaud Vincent via Digitalmars-d-learn wrote:
> On Sunday, 13 November 2016 at 23:39:37 UTC, Steven Schveighoffer
>
> wrote:
> > Note that he is declaring an int[10] inside the function and then returning it. The compiler must see that the int[10] will be returned, and so it reuses the pre-allocated buffer for returning as the same address to avoid copying.
> >
> > I would guess it's probably fine, with no dangling reference.
>
> Thank you for your comment.
>
> On my side there is still something mysterious. I one case:
>
> int[] f()
> {
>    int[10] sa;
>    foreach(int i, ref sa_i;sa){
>      sa_i=i;
>    }
>    int[] sb=sa;
>    return sb;
> }
>
> void f_test()
> {
>    auto sb=f();
>    sb[2]=100;     // Valgrind ok
> }
>
> Valgrind does not complain.
>
> But on the other case:
>
> // same int[] f() function
>
> void f_test() {
>      auto sb=f();
>      sb[2] = 100;
>      writeln(sb[2]);
>      int test[100];  // these two lines make Valgrind panicking
>      writeln(sb[2]); //
> }
>
> it complains with "Conditional jump or move depends on
> uninitialised value(s)"
>
>
> I think my two examples are creating a dangling pointer. But I would be happy to understand why Valgrind is not complaining for the first one.

I would have hoped that it would have complained about the first one. I don't know why it isn't. It definitely results in having a pointer to memory that should no longer be referenced. In the second case though, it probably complains because by declaring the static array test, there is now a static array which presumably uses some of the same memory that now destroyed static array from the call to f used. But I don't know exactly what valgrind looks at and how it makes such decisions. Regardless, there's no question that returning a dynamic array which is a slice of a static array is not something that is valid to do when that static array is a local variable. Valgrind just isn't managing to catch it in this case, unfortunately.

Hmm. Thinking on this further, it _might_ be that the

sb[2] = 100;

line is not flagged, because at that point, sb.ptr might actually be referring to memory inside of the dynamic array reference - i.e. the struct that looks something like

struct DynamicArray(T)
{
    size_t length;
    T* ptr;
}

since it would probably sit on the stack in roughly the same place that the static array had been inside of f. And if that's the case, then sb[2] is probably within the data that the dynamic array has on the stack, in which case, from valgrind's perspective, it's memory that is legitimate to access. If you tried accessing an element that would be beyond the size of the data that the dynamic array has on the stack, then maybe valgrind would flag that. I don't know. I'm just guessing, and I can't properly test it on the box that I'm typing from at the moment.

- Jonathan M Davis

November 14, 2016
On Monday, 14 November 2016 at 06:10:38 UTC, Jonathan M Davis wrote:


> I would have hoped that it would have complained about the first one. I don't know why it isn't. It definitely results in having a pointer to memory that should no longer be referenced.

Yes I would have hoped too, because it has the unfortunate consequence that we can not rely 100% that there is no dangling pointer even if valgrind said OK.

> Regardless, there's no question that returning a dynamic array which is a slice of a static array is not something that is valid to do when that static array is a local variable. Valgrind just isn't managing to catch it in this case, unfortunately.

I do agree, this is not a construct to use, I only have done this for testing purpose to see how D reacts.

Vincent

November 13, 2016
On Monday, November 14, 2016 06:19:25 Picaud Vincent via Digitalmars-d-learn wrote:
> On Monday, 14 November 2016 at 06:10:38 UTC, Jonathan M Davis
>
> wrote:
> > I would have hoped that it would have complained about the first one. I don't know why it isn't. It definitely results in having a pointer to memory that should no longer be referenced.
>
> Yes I would have hoped too, because it has the unfortunate consequence that we can not rely 100% that there is no dangling pointer even if valgrind said OK.

Well, valgrind is good, but I think that it would be a mistake to assume that it's perfect. Ideally, this should probably be reported to them, but you'd probably have to come up with equivalent C/C++ code that had the problem to report it.

> > Regardless, there's no question that returning a dynamic array which is a slice of a static array is not something that is valid to do when that static array is a local variable. Valgrind just isn't managing to catch it in this case, unfortunately.
>
> I do agree, this is not a construct to use, I only have done this for testing purpose to see how D reacts.

This is something that is _supposed_ to be caught by the compiler and flagged as @system so that if you marked your code as @safe, the compiler would complain about it, but there's a longstanding bug in the compiler that this isn't caught. However, Walter has been putting in a fair bit of work lately to improve @safe, so I expect that there's a good chance that it will be fixed at some point in the near future.

- Jonathan M Davis

November 14, 2016
On 11/14/16 12:53 AM, Picaud Vincent wrote:
> On Sunday, 13 November 2016 at 23:39:37 UTC, Steven Schveighoffer wrote:
>
>> Note that he is declaring an int[10] inside the function and then
>> returning it. The compiler must see that the int[10] will be returned,
>> and so it reuses the pre-allocated buffer for returning as the same
>> address to avoid copying.
>>
>> I would guess it's probably fine, with no dangling reference.
>
> Thank you for your comment.
>
> On my side there is still something mysterious. I one case:
>
> int[] f()
> {
>   int[10] sa;
>   foreach(int i, ref sa_i;sa){
>     sa_i=i;
>   }
>   int[] sb=sa;
>   return sb;
> }
>
> void f_test()
> {
>   auto sb=f();
>   sb[2]=100;     // Valgrind ok
> }
>
> Valgrind does not complain.

What has happened is that the stack allocated for f() (and since released) is still referenced by sb[]. In a weird way, since you haven't called any other functions, that data is still "valid"!

I'm not sure what valgrind uses to determine what data is uninitialized or allocated, but in a very real sense, this code is deterministic, and will always result in the same result. Is it good practice? No. It's very fragile, and would break as soon as you called another function or reallocated that stack space (as your second example shows).

I think valgrind is either trying not to be very assuming on how your code works, or it simply hasn't seen any real undefined behavior based on how you are reading/writing memory. Remember, valgrind has no idea what language you are using, or what compiler optimization shortcuts have been employed.

-Steve
November 14, 2016
On Monday, 14 November 2016 at 17:15:43 UTC, Steven Schveighoffer wrote:

> What has happened is that the stack allocated for f() (and since released) is still referenced by sb[]. In a weird way, since you haven't called any other functions, that data is still "valid"!

Thank you for the clarification. I am convinced, I think this is the "true" reason why Valgrind does not react.

> I'm not sure what valgrind uses to determine what data is uninitialized or allocated, but in a very real sense, this code is deterministic, and will always result in the same result. Is it good practice? No. It's very fragile, and would break as soon as you called another function or reallocated that stack space (as your second example shows).

I understand you, and for sure this is not a good practice.

I think it is time for me to ruminate on something else...
Thank you

- Vincent
1 2
Next ›   Last »