I use clang-tidy
static code analyzer
$ clang-tidy --version
LLVM (http://llvm.org/):
LLVM version 11.0.1
Optimized build.
Default target: x86_64-pc-linux-gnu
Host CPU: skylake
Given a code
#include <valarray>
std::valarray<int> f()
{
const std::valarray<int> r(0, 4UL);
return r;
}
int main() {
std::valarray<int> a{f()};
return a[1UL];
}
I've got an error Undefined or garbage value returned to caller [clang-analyzer-core.uninitialized.UndefReturn]
because clang-tidy
thinks that a[1UL]
may be uninitialized when I modify it.
main.cpp:11:3: warning: Undefined or garbage value returned to caller [clang-analyzer-core.uninitialized.UndefReturn]
return a[1UL];
^
main.cpp:10:24: note: Calling 'f'
std::valarray<int> a{f()};
^
main.cpp:6:10: note: Calling copy constructor for 'valarray<int>'
return r;
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:647:37: note: Calling '__valarray_get_storage<int>'
: _M_size(__v._M_size), _M_data(__valarray_get_storage<_Tp>(__v._M_size))
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:58:32: note: Storing uninitialized value
{ return static_cast<_Tp*>(operator new(__n * sizeof(_Tp))); }
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:647:37: note: Returning from '__valarray_get_storage<int>'
: _M_size(__v._M_size), _M_data(__valarray_get_storage<_Tp>(__v._M_size))
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:648:7: note: Calling '__valarray_copy_construct<int>'
{ std::__valarray_copy_construct(__v._M_data, __v._M_data + _M_size,
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:163:7: note: Calling '_Array_copy_ctor::_S_do_it'
_Array_copy_ctor<_Tp, __is_trivial(_Tp)>::_S_do_it(__b, __e, __o);
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:153:6: note: Assuming '__b' is null
if (__b)
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:153:2: note: Taking false branch
if (__b)
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:163:7: note: Returning from '_Array_copy_ctor::_S_do_it'
_Array_copy_ctor<_Tp, __is_trivial(_Tp)>::_S_do_it(__b, __e, __o);
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:648:7: note: Returning from '__valarray_copy_construct<int>'
{ std::__valarray_copy_construct(__v._M_data, __v._M_data + _M_size,
^
main.cpp:6:10: note: Returning from copy constructor for 'valarray<int>'
return r;
^
main.cpp:10:24: note: Returning from 'f'
std::valarray<int> a{f()};
^
main.cpp:11:12: note: Passing the value 1 via 1st parameter '__i'
return a[1UL];
^
main.cpp:11:3: note: Undefined or garbage value returned to caller
return a[1UL];
^
though, if I remove const
from the r
initialization, the code works fine.
Another way of "fixing" this is to set a size less than 4UL
— for example, 2UL
or 3UL
. Even 1UL
fits, though it's false-negative and I don't hope that static analyzer finds everything in the world.
The complicated copy constructor could be a problem but consider the following example
#include <cassert>
#include <iostream>
#include <valarray>
struct A {
std::valarray<int> value;
explicit A(const std::size_t half_size) : value(0, 2UL * half_size) {
assert(half_size == 2UL);
}
};
int main() {
std::size_t size;
std::cin >> size;
A a{size};
++a.value[1UL];
return 0;
}
with a very similar problem
main.cpp:16:3: warning: The expression is an uninitialized value. The computed value will also be garbage [clang-analyzer-core.uninitialized.Assign]
++a.value[1UL];
^
main.cpp:15:5: note: Calling constructor for 'A'
A a{size};
^
main.cpp:7:45: note: Calling constructor for 'valarray<int>'
explicit A(const std::size_t half_size) : value(0, 2UL * half_size) {
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:632:29: note: Calling '__valarray_get_storage<int>'
: _M_size(__n), _M_data(__valarray_get_storage<_Tp>(__n))
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:58:32: note: Storing uninitialized value
{ return static_cast<_Tp*>(operator new(__n * sizeof(_Tp))); }
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:632:29: note: Returning from '__valarray_get_storage<int>'
: _M_size(__n), _M_data(__valarray_get_storage<_Tp>(__n))
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:633:7: note: Calling '__valarray_fill_construct<int>'
{ std::__valarray_fill_construct(_M_data, _M_data + __n, __t); }
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:127:7: note: Calling '_Array_init_ctor::_S_do_it'
_Array_init_ctor<_Tp, __is_trivial(_Tp)>::_S_do_it(__b, __e, __t);
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:118:9: note: Assuming '__b' is equal to '__e'
while (__b != __e)
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:118:2: note: Loop condition is false. Execution continues on line 118
while (__b != __e)
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/valarray_array.h:127:7: note: Returning from '_Array_init_ctor::_S_do_it'
_Array_init_ctor<_Tp, __is_trivial(_Tp)>::_S_do_it(__b, __e, __t);
^
/usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/valarray:633:7: note: Returning from '__valarray_fill_construct<int>'
{ std::__valarray_fill_construct(_M_data, _M_data + __n, __t); }
^
main.cpp:7:45: note: Returning from constructor for 'valarray<int>'
explicit A(const std::size_t half_size) : value(0, 2UL * half_size) {
^
main.cpp:8:12: note: Assuming 'half_size' is equal to 2
assert(half_size == 2UL);
^
/usr/include/assert.h:93:27: note: expanded from macro 'assert'
(static_cast <bool> (expr) \
^
main.cpp:8:12: note: 'half_size' is equal to 2
assert(half_size == 2UL);
^
/usr/include/assert.h:93:27: note: expanded from macro 'assert'
(static_cast <bool> (expr) \
^
main.cpp:8:5: note: '?' condition is true
assert(half_size == 2UL);
^
/usr/include/assert.h:93:7: note: expanded from macro 'assert'
(static_cast <bool> (expr) \
^
main.cpp:15:5: note: Returning from constructor for 'A'
A a{size};
^
main.cpp:16:13: note: Passing the value 1 via 1st parameter '__i'
++a.value[1UL];
^
main.cpp:16:3: note: The expression is an uninitialized value. The computed value will also be garbage
++a.value[1UL];
^
I've tried reading LLVM source code but don't know which file to monitor, so I didn't succeed in finding the false-positive cause (if it's a false positive).
Changing valarray
to vector
fixes both examples.