TArray::Remove bug

When calling TArray::Remove in an array with a single object (TArray store copies of objects), the CheckAddress() function report as following:

Assertion failed: Addr < GetData() || Addr >= (GetData() + ArrayMax) [File:C:\Program Files\Epic Games\4.10\Engine\Source\Runtime\Core\Public\Containers\Array.h] [Line: 1409]
Attempting to add a container element (0x8031bba0) which already comes from the container (0x8031bba0, ArrayMax: 4)!

If you notice, the error doesn’t even make sense. The object pointer lies well between GetData() and GetData() + ArrayMax range. I solved the problem by calling RemoteAt, since it works properly. Following is the code that triggers the bug:

namespace
{
  struct PathTile
  {
    TArray<ABaseTile*> pathToThis;
    ABaseTile* tile;
    int g;
    int h;
    int f;

    PathTile(ABaseTile* t, int g, int h, TArray<ABaseTile*> currentPath)
      : tile(t), g(g), h(h), f(g + h)
    {
      check(tile != nullptr);

      pathToThis = currentPath;
      pathToThis.Add(tile);
    }

    bool operator == (const PathTile& other)
    {
      UE_LOG(LogTemp, Warning, TEXT("EQUALS"));
      return tile == other.tile;
    }
  };

  PathTile extractTileWithLowestScore(TArray<PathTile>& tiles)
  {
    check(tiles.Num() > 0);

    PathTile* lowestScore = &tiles[0];
    for (int i = 1; i < tiles.Num(); ++i)
    {
      PathTile* tile = &tiles[i];
      if (tile->f < lowestScore->f)
        lowestScore = tile;
    }

    // Copy tile properties.
    PathTile tileCopy(*lowestScore);

    // Remove from list. ** Where it triggers the bug **
    tiles.Remove(*lowestScore);

    // Return copy.
    return tileCopy;
  }
}

And inside my function:

  TArray<PathTile> openList;
  openList.Add(PathTile(startTile, 0, getHScore(startTile, endTile), TArray<ABaseTile*>()));

  while (openList.Num() > 0)
  {
    PathTile tile = extractTileWithLowestScore(openList);
  }

The error is not related to index, but memory address related. GetData() returns pointer which points to first memory address of array and if you add ArrayMax it will point to address of last item. If you don’t know that already, pointer is a memory address varable and it behaves same as int… yes you can do normal math on it and you have example of that here.

So what happens here is you create pointer of array item, which points to array memory space, then you try to remove it the argument of Remove function is reference, so memory address of item you give it same as pointer you used (which points to array). Remove function (and also Add and Insert functions) have check if address of argument not the range of array in memory, if it is not it rise the error. This was added probably to avoid buggy behavior of pointer, in which it starts to point at wrong item because things moved around in array. But i don’t see point to do so remove, maybe to force developer to not direly address items in array to avoid unsafe situations, since if that Remove function was successful the pointer would point something else or be invalid (which is state that is not checkable).

RemoveAt indeed would solve this problem (in fact it much faster), also not using pointer is good solution, you could use varable that you copied which would naturally have diffrent memoery address then array.

I am aware of how pointers, references and arrays work (Hence why I copied the element at line 40 and returned it as a copy, instead of a ref). I am also aware that RemoveAt should be faster, since it doesn’t need to look at the element position on the array or its copies.

So what happens here is you create pointer of array item, which points to array memory space, then you try to remove it the argument of Remove function is reference, so memory address of item you give it same as pointer you used (which points to array). Remove function (and also Add and Insert functions) have check if address of argument not the range of array in memory, if it is not it rise the error. This was added probably to avoid buggy behavior of pointer, in which it starts to point at wrong item because things moved around in array. But i don’t see point to do so remove, maybe to force developer to not direly address items in array to avoid unsafe situations, since if that Remove function was successful the pointer would point something else or be invalid (which is state that is not checkable)

I understand your point about safety on the operations (checking the range of data to make sure the user doesn’t end with an invalid pointer at hand - which would happen in a loop scenario), but it still doesn’t make any sense to force the user to call Remove only on non-array-addressable references. I’d say the API should have at least a comment stating that this is invalid behavior or at least accept parameters as a copy (instead of const ref), and that’s why I am filling this bug. Maybe I explained myself wrong, because what I’d expect is a better documentation on the function about the const ref parameter or to accept elements from the array memory space itself:

Checking the docs at TArray::Remove | Unreal Engine 5.2 Documentation

int32 Remove
(
    const ElementType & Item
)

doesn’t show how my code could be wrong.

Well don’t expect that documentation tells you all case scenarios that may go wrong and that error is there to inform you :stuck_out_tongue: having text description in check() is quite rare in engine code. Either way in order to raport bug you need to submit question in “Bug Raports” category, i will move your question there now (so you dont need to do anything ;).

Hey nightz-

Was 's information helpful for you? If you are still having this issue can you explain where you’ve added this code (what class/function does the code belong to)? Line 50 above mentions “inside my function” - what function is this referring to?