Error CS353 When it doesn't make sense for this situation

A result of a stackalloc expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the containing method however it isn't exposed out of the method, I will post the code in another message as it is a lil long
15 Replies
Sherbert Lemon
Sherbert LemonOP2y ago
public unsafe int[] GetConnectedPoints(int index)
{
const int allocationBlocks = 5;

Span<int> neighboursSpan = stackalloc int[allocationBlocks]; // "allocate" allocationBlocks, if we overrun we can increase this
int currIndex = 0; // this is pretty much a stack list, because we are not working with very large int[]s it doesn't make sense to heap allocate, also gc isn't necessary as we can clean up ourselves

Span<int> memoryBlocks = null; // this shouldn't normally be used as the allocation block should be plenty, but to remove the chance of errors this is a backup
int currBlock = 0;

for (int i = 0; i < faces.Length; i++)
{
Face curr = faces[i];
if (currIndex + 2 > neighboursSpan.Length)
{
if (currBlock == 0)
{
// ReSharper disable once StackAllocInsideLoop
memoryBlocks = stackalloc int[allocationBlocks];
}

// ReSharper disable once StackAllocInsideLoop - ignore warning - it will be fine as we are only doing it in a specific circumstance in a for loop
int* increasedAllocBlock =
stackalloc int[allocationBlocks]; // the pointers work without pinning because stack allocated data is always pinned
memoryBlocks[(currIndex / allocationBlocks)] = (int)&neighboursSpan;
memoryBlocks[(currIndex / allocationBlocks) + 1] = (int)increasedAllocBlock;

neighboursSpan = new Span<int>(increasedAllocBlock, allocationBlocks);

currBlock++;
}
public unsafe int[] GetConnectedPoints(int index)
{
const int allocationBlocks = 5;

Span<int> neighboursSpan = stackalloc int[allocationBlocks]; // "allocate" allocationBlocks, if we overrun we can increase this
int currIndex = 0; // this is pretty much a stack list, because we are not working with very large int[]s it doesn't make sense to heap allocate, also gc isn't necessary as we can clean up ourselves

Span<int> memoryBlocks = null; // this shouldn't normally be used as the allocation block should be plenty, but to remove the chance of errors this is a backup
int currBlock = 0;

for (int i = 0; i < faces.Length; i++)
{
Face curr = faces[i];
if (currIndex + 2 > neighboursSpan.Length)
{
if (currBlock == 0)
{
// ReSharper disable once StackAllocInsideLoop
memoryBlocks = stackalloc int[allocationBlocks];
}

// ReSharper disable once StackAllocInsideLoop - ignore warning - it will be fine as we are only doing it in a specific circumstance in a for loop
int* increasedAllocBlock =
stackalloc int[allocationBlocks]; // the pointers work without pinning because stack allocated data is always pinned
memoryBlocks[(currIndex / allocationBlocks)] = (int)&neighboursSpan;
memoryBlocks[(currIndex / allocationBlocks) + 1] = (int)increasedAllocBlock;

neighboursSpan = new Span<int>(increasedAllocBlock, allocationBlocks);

currBlock++;
}
the rest is irrelevant but I will add it for context
if (curr.a == i)
{
neighboursSpan[currIndex] = curr.b;
neighboursSpan[currIndex + 1] = curr.c;

currIndex += 2;
}
else if (curr.b == i)
{
neighboursSpan[currIndex] = curr.a;
neighboursSpan[currIndex + 1] = curr.c;

currIndex += 2;
}
else if (curr.c == i)
{
neighboursSpan[currIndex] = curr.a;
neighboursSpan[currIndex + 1] = curr.b;

currIndex += 2;
}
}

if (memoryBlocks != null) // null but rider didn't like null
{
int[] neighbours = new int[currIndex + allocationBlocks * currBlock];
for (int i = 0; i < currBlock; i++)
{
neighbours.CopyTo(new Span<int>((int*)memoryBlocks[i], allocationBlocks));
}

return neighbours;
}
else
{
return neighboursSpan.ToArray();
}
}
if (curr.a == i)
{
neighboursSpan[currIndex] = curr.b;
neighboursSpan[currIndex + 1] = curr.c;

currIndex += 2;
}
else if (curr.b == i)
{
neighboursSpan[currIndex] = curr.a;
neighboursSpan[currIndex + 1] = curr.c;

currIndex += 2;
}
else if (curr.c == i)
{
neighboursSpan[currIndex] = curr.a;
neighboursSpan[currIndex + 1] = curr.b;

currIndex += 2;
}
}

if (memoryBlocks != null) // null but rider didn't like null
{
int[] neighbours = new int[currIndex + allocationBlocks * currBlock];
for (int i = 0; i < currBlock; i++)
{
neighbours.CopyTo(new Span<int>((int*)memoryBlocks[i], allocationBlocks));
}

return neighbours;
}
else
{
return neighboursSpan.ToArray();
}
}
The error is on the line
memoryBlocks = stackalloc int[allocationBlocks];
memoryBlocks = stackalloc int[allocationBlocks];
I might just not use spans for this situation as I am not entirely sure but it would still be good to know what was going wrong I would check the il but it is a full on error so I can't I did the il with sharplab and that worked and it didn't seem to expose ah I made a rather silly mistake, the copyto has the destination as its param not the data nope that didn't fix it this one is really confusing me, I commented out all the return values and made it return null, the only parameter in is an integer and it is still saying that it is exposed
333fred
333fred2y ago
Declare memoryBlocks as scoped scoped Span<int> memoryBlocks... The default safe-to-escape scope is not scoped And you're not initializing it with something that would narrow it (like assigning a stackalloc in the initializer)
Sherbert Lemon
Sherbert LemonOP2y ago
I can't use scoped as my language version for this can't be high enough but I could assign it, is there any way to have it without allocating it, preferably it should be very rarely allocated, but I could allocate it if I have to also what is scoped, I haven't come across that before and I can't see it in the docs ah found it in the c# 11 page
333fred
333fred2y ago
Do a stackalloc int[0]; I'm pretty sure that works
Sherbert Lemon
Sherbert LemonOP2y ago
yeah ok will do, why is it that it has to be allocated? that does work thanks
333fred
333fred2y ago
.
Sherbert Lemon
Sherbert LemonOP2y ago
I don't quite understand
333fred
333fred2y ago
By default, the local can escape the method That's it Doesn't matter whether it does. It can You can narrow that default by assigning something that not safe-to-escape Or, in C# 11, you can declare the local as scoped
Sherbert Lemon
Sherbert LemonOP2y ago
ah ok that makes more sense, why isn't it implicitly not safe to escape by already being defined as a valuetype in the locals
333fred
333fred2y ago
Being a valuetype has nothing to do with safe-to-escape
Sherbert Lemon
Sherbert LemonOP2y ago
it should also be defined as a Span<int> there too also wouldn't it
333fred
333fred2y ago
Span<char> s = " test"; is perfectly fine to escape the method
Sherbert Lemon
Sherbert LemonOP2y ago
what isn't safe to escape? actually a better question would be, what does it mean for something to escape the method
333fred
333fred2y ago
Span<char> Get() => "Test"; // escapes, it's heap data, this is fine Span<char> Get() => stackalloc char[1] { 'c' }; // escapes, oops
Sherbert Lemon
Sherbert LemonOP2y ago
ahhh ok thanks that clarifies it
Want results from more Discord servers?
Add your server