Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No array bounds check when using auto

Tags:

c++

clang

clang++

When having this code compiling with -Warray-bounds. I get a warning when declaring array2 array index 3 is past the end of the array (which contains 3 elements). But not when declaring array1 even though it has to be the same type thus carrying the same size information. Is this a bug in clang?

enum class Format : int {
  Off = 55,
  FormatA = 66,
  FormatB = 77,
};

inline Format (&AllFormats())[3] {
  static Format values[] = {
    Format::Off,
    Format::FormatA,
    Format::FormatB
  };
  return values;
}

int main()
{
    auto array1 = AllFormats();    
    auto v3 = array1[3];

    Format (&array2)[3] = AllFormats();    
    v3 = array2[3];
}
like image 870
schoetbi Avatar asked Jun 03 '19 13:06

schoetbi


People also ask

Why array bounds checking is not available in C?

This is due to the fact that C++ does not do bounds checking. Languages like Java and python have bounds checking so if you try to access an out of bounds element, they throw an error. C++ design principle was that it shouldn't be slower than the equivalent C code, and C doesn't do array bounds checking.

Is there bound checking in array?

Array bound checking refers to determining whether all array references in a program are within their declared ranges. This checking is critical for software verification and validation because subscripting arrays beyond their declared sizes may produce unexpected results, security holes, or failures.

Does Java check array bounds?

Abstract: As a type-safe program language, Java requires bounds checks of array accesses. Whenever an array element is accessed, a cmp (compare) instruction is executed to check whether the index value is within the valid bounds. Array bounds checks may prevent many useful optimizations because of precise exception.

Do Native arrays have bounds checking C++?

Note that C and C++ do not do bounds checking on arrays, so stuff like that isn't going to be caught at compile or run time. No, Undefined behavior "works in your favor" when it crashes cleanly.


2 Answers

even though it has to be the same type

You’d think that. But if you check, you’ll find that they actually don’t have the same type:

std::cout << typeid(array1).name() << "\n";
std::cout << typeid(array2).name() << "\n";
P6Format
A3_6Format

Oops. The array returned by AllFormats decays to a pointer when assigned to an auto variable because that’s how the type deduction rules for auto work. Compare:

int& foo() {
    static int x = 42;
    return x;
}

auto x = foo(); // Type of `x` is `int`, not `int&`.

To prevent this, declare array1 as auto& or auto&&.

like image 165
Konrad Rudolph Avatar answered Sep 22 '22 22:09

Konrad Rudolph


But not at line 16 even though it has to be the same type

Assuming by it you refer to auto array1 = AllFormats(), then it doesn't have the same type. auto is never deduced to be a reference, so array1 is not a reference. It is a non-reference, and is deduced to be the decayed result, i.e. a pointer to Format.

As the pointer type doesn't carry information about the size of the pointed array, the compiler wasn't able to prove that the subscript operator overflows the array.

To declare a reference, you can use either:

auto&          array1 = AllFormats(); // 1.
auto&&         array1 = AllFormats(); // 2.
decltype(auto) array1 = AllFormats(); // 3.
  1. Declares explicitly an lvalue reference.
  2. Declares a universal reference, which collapses into an lvalue reference, because AllFormats returns an lvalue reference. It would be an rvalue reference if AllFormats returned Format&&.
  3. auto type deduction uses different rules from decltype deduction. One key difference is that auto is never a reference, while decltype(E); may be a reference, depending on the expression E. decltype(auto) var = E allows a declaration using the decltype rules as if decltype(E) was used.
like image 24
eerorika Avatar answered Sep 22 '22 22:09

eerorika