llvm / llvm-project

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

dynamic_cast throws off static analyzer nullptr analysis #47869

Open tlemo opened 3 years ago

tlemo commented 3 years ago
Bugzilla Link 48525
Version unspecified
OS All
CC @devincoughlin,@haoNoQ

Extended Description

// https://godbolt.org/z/r1WEPs

include

include

struct Base { virtual ~Base() = default; virtual bool test() const; };

struct Deriv : public Base {};

std::vector<Base*> foo();

void bar() { const auto& inputs = foo(); for(auto ptr : inputs) {

if 1 // turn this to 0 to check the plain ptr->test() handling w/o the first if

    if(auto deriv = dynamic_cast<Deriv*>(ptr)) {
        printf("Deriv: %p\n", deriv);
    } else
    #endif
    if (ptr->test()) {
        printf("Base: %p\n", ptr);
    }
}

}

:22:13: warning: Called C++ object pointer is null [clang-analyzer-core.CallAndMessage] if (ptr->test()) { ^ :16:9: note: 'ptr' initialized here for(auto ptr : inputs) { ^ :18:17: note: Assuming 'deriv' is null if(auto deriv = dynamic_cast<Deriv>(ptr)) { ^ :18:9: note: Taking false branch if(auto deriv = dynamic_cast<Deriv>(ptr)) { ^ :22:13: note: Called C++ object pointer is null if (ptr->test()) { ^ 1 warning generated.

llvmbot commented 3 years ago

Is this a duplicat of #​41463?

haoNoQ commented 3 years ago

Yeah, we need to cut this execution path, at least until we're able to do the right thing by writing down the exact constraint on the symbol produced by the dynamic cast without introducing unnecessary state splits and expecting the constraint solver to be able to handle it. Thanks!

llvmbot commented 3 years ago

This is an issue with the clang static analyser, not clang tidy

tlemo commented 3 years ago

assigned to @haoNoQ

fukanchik commented 1 year ago

Shorter code to reproduce this:

struct A
{
  int foo;
  virtual ~A(){}
};

struct B: public A
{
  int bar;
  virtual ~B(){}
};

int foo(A* ptr)
{
  B* b = dynamic_cast<B*>(ptr);
  if (b)
    return b->bar;
  return ptr->foo;
}

output:

x.cpp:18:10: warning: Access to field 'foo' results in a dereference of a null pointer (loaded from variable 'ptr') [core.NullDereference]
  return ptr->foo;
         ^~~~~~~~
1 warning generated.