llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.15k stars 12.03k forks source link

Clang-tidy considers a data member to be uninitialized #50289

Open 73dc6587-d003-4d5d-9fd0-2c50994fb6f3 opened 3 years ago

73dc6587-d003-4d5d-9fd0-2c50994fb6f3 commented 3 years ago
Bugzilla Link 50945
Version unspecified
OS Linux

Extended Description

Following snippet reproduces the bug

tuple_storage variadic constructor correctly performs aggregate initialization of all its base classes, but clang-tidy complains that they are not initialized.

include

include

template <size_t N, typename T> struct indexed_elem_storage {

ifdef CLANG_TIDY_WORKAROUND

indexed_elem_storage() = default;

constexpr indexed_elem_storage(T val) : value(val) {}

endif

T value{}; };

template <typename ...Ts> struct tuple_storage;

template <size_t ...Ints, typename ...Ts> struct tuple_storage<std::index_sequence, Ts...> : indexed_elem_storage<Ints, Ts>... { tuple_storage() = default;

constexpr tuple_storage(Ts ...args) : indexed_elem_storage<Ints, Ts>{args}... {}

template <size_t N, typename T> static constexpr T& get(indexed_elem_storage<N, T>& base) { return base.value; } };

template <typename ...Ts> struct tuple { constexpr tuple(Ts... args) : storage(args...) {}

template <size_t N>
constexpr auto& get()
{
    return storage_t::template get<N>(storage);
}

private: using storage_t = tuple_storage<std::index_sequence_for, Ts...>; storage_t storage; };

constexpr bool test() { tuple<int, char, float> t = { 1, 'c', 2.0f }; return t.get<1>() == 'c'; }

int main() { static_assert(test(), ""); }

PiotrZSL commented 1 year ago

This is static analyser issues, clang-analyzer-core.UndefinedBinaryOperatorResult

/fpwork/llvm-project3/1.cpp:59:23: warning: The left operand of '==' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
    return t.get<1>() == 'c';
           ~~~~~~~~~~ ^
/fpwork/llvm-project3/1.cpp:58:33: note: Calling constructor for 'tuple<int, char, float>'
    tuple<int, char, float> t = { 1, 'c', 2.0f };
                                ^~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:42:9: note: Calling constructor for 'tuple_storage<std::integer_sequence<unsigned long, 0, 1, 2>, int, char, float>'
      : storage(args...)
        ^~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:29:5: note: Returning without writing to 'this->value'
   {}
    ^
/fpwork/llvm-project3/1.cpp:42:9: note: Returning from constructor for 'tuple_storage<std::integer_sequence<unsigned long, 0, 1, 2>, int, char, float>'
      : storage(args...)
        ^~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:43:6: note: Returning without writing to 'this->storage.value'
    {}
     ^
/fpwork/llvm-project3/1.cpp:58:33: note: Returning from constructor for 'tuple<int, char, float>'
    tuple<int, char, float> t = { 1, 'c', 2.0f };
                                ^~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:59:12: note: Calling 'tuple::get'
    return t.get<1>() == 'c';
           ^~~~~~~~~~
/fpwork/llvm-project3/1.cpp:48:16: note: Calling 'tuple_storage::get'
        return storage_t::template get<N>(storage);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:34:7: note: Returning without writing to 'base.value'
      return base.value;
      ^
/fpwork/llvm-project3/1.cpp:34:7: note: Returning pointer (reference to 't.storage.value')
      return base.value;
      ^~~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:48:16: note: Returning from 'tuple_storage::get'
        return storage_t::template get<N>(storage);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:48:9: note: Returning pointer (reference to 't.storage.value')
        return storage_t::template get<N>(storage);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/fpwork/llvm-project3/1.cpp:59:12: note: Returning from 'tuple::get'
    return t.get<1>() == 'c';
           ^~~~~~~~~~
/fpwork/llvm-project3/1.cpp:59:23: note: The left operand of '==' is a garbage value
    return t.get<1>() == 'c';
           ~~~~~~~~~~ ^
llvmbot commented 1 year ago

@llvm/issue-subscribers-clang-static-analyzer

haoNoQ commented 1 year ago

Reproduced: https://godbolt.org/z/PvqcaG9Mr

<source>:41:35: note: Calling constructor for 'tuple_storage<std::integer_sequence<unsigned long, 0, 1, 2>, int, char, float>'
    constexpr tuple(Ts... args) : storage(args...) {}
                                  ^~~~~~~~~~~~~~~~
<source>:29:6: note: Returning without writing to 'this->value'
    {}
     ^

This is definitely wrong and surprising, I wouldn't expect basic constructors to act up this way. (And they're really basic given that we see through templates transparently.) Need to take a closer look.

#ifdef CLANG_TIDY_WORKAROUND

Note that you can use #ifdef __clang_analyzer__ to find out if clang analyzer is running, this may help you automate your workarounds!

selassje commented 1 year ago

Here is a smaller example with clang-17

https://godbolt.org/z/TTTaGsrdx