[llvm-bugs] [Bug 39028] New: Scan-build reports a false positive nullptr dereference because it misses calls to template member functions within a copy constructor of a class template

via llvm-bugs llvm-bugs at lists.llvm.org
Thu Sep 20 14:22:49 PDT 2018


https://bugs.llvm.org/show_bug.cgi?id=39028

            Bug ID: 39028
           Summary: Scan-build reports a false positive nullptr
                    dereference because it  misses calls to template
                    member functions within a copy constructor of a class
                    template
           Product: clang
           Version: 7.0
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: Static Analyzer
          Assignee: dcoughlin at apple.com
          Reporter: thomas.ullmann at mpibpc.mpg.de
                CC: llvm-bugs at lists.llvm.org

Created attachment 20901
  --> https://bugs.llvm.org/attachment.cgi?id=20901&action=edit
The full sources and output of scan-build including the html-output

The example is not too short, but I couldn't reduce it further.

I have a class template for a 1D-array defined in a header file
and a main file that includes the header. An instance of the array
is created and copied via the copy constructor to a second instance
of the array. Scan-build reports a false positive "Returning null
reference" if I access an element of the array copy.

The problem only occurs if the array class is defined in a separate
header and if the copy constructor calls a template member function
that does the actual copying. Moving the class template definition
to the main file or substituting the called template member function
with a non-templated version lets the report vanish.

A tar-ball of the complete test input and output including
the HTML report is attached.

/*---------------------------------------------------------
   The header for the array class array_1d.h
-----------------------------------------------------------*/
#ifndef ARRAY_1D_H
#define ARRAY_1D_H

#include <memory>

template <class T = double, class Allocator = std::allocator<T> >
class Array1D
{
    private:
        size_t      length1_ = 0;
        T*          arr_     = nullptr;
        Allocator   allocator_;
    public:
        Array1D() : length1_(0), arr_(nullptr) {}

        explicit Array1D(const size_t l)
        {
            length1_ = l;
            allocateArray();
        }

        Array1D(const size_t l, const T& ini)
            : Array1D(l)
        {
             for (size_t i = 0; i < length1_; ++i)
            {
                arr_[i] = ini;
            }
        }

        Array1D(const Array1D& ini)
          : Array1D()
        {
            initArray(ini); // this call is missed/not followed
        }

/*
        // The error does not occur with this non-templated function
        void initArray(const Array1D& ini)
        {
            length1_ = ini.length1_;
            allocateArray();
            for (size_t i = 0; i < length1_; ++i)
            {
                arr_[i] = ini.arr_[i];
            }
        }
*/

        // The error only occurs with this templated function and
        // if the class is not directly defined in the main file test.cpp.
        template <typename TForeign, class AForeign = std::allocator<TForeign>
>
        void initArray(const Array1D<TForeign, AForeign> &ini)
        {
            if (ini.arr_ != nullptr)
            {
                deallocateArray();
                if (ini.length1_ != 0)
                {
                    length1_ = ini.length1_;
                    allocateArray();
                }
                if (arr_ != nullptr && ini.arr_ != nullptr)
                {
                    for (size_t i = 0; i < length1_; ++i)
                    {
                        arr_[i] = static_cast<T>(ini(i));
                    }
                }
            }
        }

       ~Array1D() { deallocateArray(); }

        T& operator()(const size_t i)
        {
            return arr_[i];
        }
        const T& operator()(const size_t i) const
        {
            return arr_[i];
        }


   private:
        void allocateArray()
        {
             if (arr_ == nullptr)
             {
                 arr_  = std::allocator_traits<Allocator>::allocate(allocator_,
length1_);
             }
             for (size_t i = 0; i < length1_; ++i)
             {
                 std::allocator_traits<Allocator>::construct(allocator_,
&arr_[i], T());
             }
        }
        void deallocateArray()
        {
             if (arr_ != nullptr)
             {
                 for (size_t i = 0; i < length1_; ++i)
                 {
                     std::allocator_traits<Allocator>::destroy(allocator_,
&arr_[i]);
                 }
                 std::allocator_traits<Allocator>::deallocate(allocator_, arr_,
length1_);
            }
            length1_ = 0;
        }
};
#endif

/*---------------------------------------------------------
 The main file test.cpp
-----------------------------------------------------------*/

#include <iostream>
#include "array_1d.h"

int main ()
{
    Array1D<int> testArr1(10ul, 1);

    std::cout << "testArr1(0) = " << testArr1(0) << std::endl;

    // copy construct testArr2 from testArr1
    Array1D<int> testArr2(testArr1);

    // nullptr dereference reported here, although the copy succeeded
    std::cout << "testArr2(0) = " << testArr2(0) << std::endl;

    return 0;
}

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180920/c03c4b95/attachment.html>


More information about the llvm-bugs mailing list