Jump to page: 1 2
Thread overview
Actual lifetime of static array slices?
Nov 15
Elfstone
Nov 15
Elfstone
Nov 15
Elfstone
Nov 15
Elfstone
November 15

I failed to find any documentation, except dynamic array slices will be taken care of by GC, but I assume it's not the case with static arrays.

But the code bellow doesn't behave as I expected.

int[] foo()
{
	int[1024] static_array;
	// return static_array[]; // Error: returning `static_array[]` escapes a reference to local variable `static_array`
    return null;
}

class A
{
	this(int[] inData)
	{
		data = inData;
	}

	int[] data;
}

void main()
{
	int[] arr;
	A a;
	{
		int[1024] static_array;
		arr = aSlice; // OK
		a = new A(aSlice); // OK
		arr = foo();
		//arr = foo();

	}
}

By assigning aSlice to arr or a, it seemingly escapes the scope, I thought there'd be errors, but the code compiles just fine.

Is it really safe though?

November 15

On Tuesday, 15 November 2022 at 02:26:41 UTC, Elfstone wrote:

>

I failed to find any documentation, except dynamic array slices will be taken care of by GC, but I assume it's not the case with static arrays.

A slice is a view on the existing memory owned by the original array. No allocations are made for the slice. The GC will track all references to the memory allocated for a dynamic array, so as long as any slices remain alive, so will the original memory.

Static arrays are allocated on the stack and become invalid when they leave a function scope. In turn, so would any slices or other pointers that reference that stack memory.

>

But the code bellow doesn't behave as I expected.

int[] foo()
{
	int[1024] static_array;
	// return static_array[]; // Error: returning `static_array[]` escapes a reference to local variable `static_array`
    return null;
}

class A
{
	this(int[] inData)
	{
		data = inData;
	}

	int[] data;
}

void main()
{
	int[] arr;
	A a;
	{
		int[1024] static_array;
		arr = aSlice; // OK
		a = new A(aSlice); // OK
		arr = foo();
		//arr = foo();

	}
}

By assigning aSlice to arr or a, it seemingly escapes the scope, I thought there'd be errors, but the code compiles just fine.

Is it really safe though?

It's not the scope that matters here. It's the stack. Memory allocated in the inner scope uses the function stack, so it's all valid until the function exits.

November 15

On Tuesday, 15 November 2022 at 02:26:41 UTC, Elfstone wrote:

>

By assigning aSlice to arr or a, it seemingly escapes the scope, I thought there'd be errors, but the code compiles just fine.

Is it really safe though?

No, it's not safe. You can add @safe: line in the beginning of your program and it will fail to compile (after renaming static_array to aSlice):

test.d(27): Error: address of variable `aSlice` assigned to `arr` with longer lifetime

By default everything is assumed to be @system and the compiler silently allows you to shoot yourself in the foot. See https://dlang.org/spec/memory-safe-d.html

November 15

On Tuesday, 15 November 2022 at 02:49:55 UTC, Mike Parker wrote:

>

It's not the scope that matters here. It's the stack. Memory allocated in the inner scope uses the function stack, so it's all valid until the function exits.

And that was just so, so wrong. Of course destructors get called when scopes exit, etc.

November 15

On Tuesday, 15 November 2022 at 02:50:44 UTC, Siarhei Siamashka wrote:

>

On Tuesday, 15 November 2022 at 02:26:41 UTC, Elfstone wrote:

>

By assigning aSlice to arr or a, it seemingly escapes the scope, I thought there'd be errors, but the code compiles just fine.

Is it really safe though?

No, it's not safe. You can add @safe: line in the beginning of your program and it will fail to compile (after renaming static_array to aSlice):

test.d(27): Error: address of variable `aSlice` assigned to `arr` with longer lifetime

By default everything is assumed to be @system and the compiler silently allows you to shoot yourself in the foot. See https://dlang.org/spec/memory-safe-d.html

Thanks, @safe works my first code, but the following code still compiles.

class A
{
	@safe
	this(int[] inData)
	{
		data = inData;
	}

	int[] data;
}

@safe
int[] foo()
{
	int[1024] static_array;
	// return static_array[]; // Error: returning `static_array[]` escapes a reference to local variable `static_array`
return null;
}

@safe
A bar()
{
	int[1024] static_array;
	return new A(static_array[]);
}

@safe
void main()
{
	auto a = bar();
	writeln(a.data); // OK, but writes garbage
}

So the compiler detects escaping in foo() but not in bar(), this doesn't look right.

Is there a way to tell whether a slice is from a dynamic array or a static array?

November 15

On Tuesday, 15 November 2022 at 03:05:30 UTC, Elfstone wrote:

>

So the compiler detects escaping in foo() but not in bar(), this doesn't look right.

The compiler can detect it with -dip1000 command line option.

>

Is there a way to tell whether a slice is from a dynamic array or a static array?

For debugging purposes? Maybe find the stack boundaries and check if the address is in stack?

November 15

On Tuesday, 15 November 2022 at 03:18:17 UTC, Siarhei Siamashka wrote:

>

On Tuesday, 15 November 2022 at 03:05:30 UTC, Elfstone wrote:

>

So the compiler detects escaping in foo() but not in bar(), this doesn't look right.

The compiler can detect it with -dip1000 command line option.

>

Is there a way to tell whether a slice is from a dynamic array or a static array?

For debugging purposes? Maybe find the stack boundaries and check if the address is in stack?

Great! This should be a builtin feature!

I just checked the DIP list and #1000 is marked superseded. Any idea what supersedes it?

November 15
On 15/11/2022 5:10 PM, Elfstone wrote:
> I just checked the DIP list and #1000 is marked superseded. Any idea what supersedes it?

The implementation.
November 15
On Tuesday, 15 November 2022 at 04:10:37 UTC, rikki cattermole wrote:
> On 15/11/2022 5:10 PM, Elfstone wrote:
>> I just checked the DIP list and #1000 is marked superseded. Any idea what supersedes it?
>
> The implementation.

Cool.
November 14
On 11/14/22 19:05, Elfstone wrote:

>      @safe
>      int[] foo()
>      {
>          int[1024] static_array;
>          // return static_array[]; // Error: returning `static_array[]`
> escapes a reference to local variable `static_array`

That is trivial for the compiler to catch.

>      return null;
>      }
>
>      @safe
>      A bar()
>      {
>          int[1024] static_array;
>          return new A(static_array[]);

That one requires the computer to analyze the code to a deeper level. Yes, we are passing a slice to the A constructor but we don't know whether the constructor will store the slice or simply use it. Even the following can be safe but impossible to detect by the compiler:

class A
{
    @safe
    this(int[] inData)
    {
        data = someCondition() ? new int[42] : inData; // (1)
        // ...
        if (someOtherCondition()) {
            data = null;                               // (2)
        }
    }
    // ...
}

(1) Whether we use inData depends on someCondition(). It may be so that bar() is never called depending on someCondition() in the program at all.

(2) data may never refer to 'static_array' after the constructor exits depending on someOtherCondition()

The code above may not be using good coding practices and humans may see the bugs if there are any but only a slow (infinetely?) compiler can see through all the code. (I think @live will help with these cases.) Further, the support for "separate compilation" makes it impossible to see through function boundaries.

Additionally, we don't want the compiler to force us to copy all stack variables just in case.

In summary, you are right but the compiler cannot do anything about it in all cases and we wouldn't want it to spend infinite amount of time to try to determine everything.

Ali

« First   ‹ Prev
1 2