================
@@ -123,6 +123,13 @@ void VirtualCallChecker::checkPreCall(const CallEvent 
&Call,
   if (!ObState)
     return;
 
+  // If the class whose constructor/destructor we're in is marked final,
+  // virtual dispatch is safe because there can be no derived classes.
+  const auto *LCtx = C.getLocationContext();
+  const auto *CurMethod = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
+  if (CurMethod && CurMethod->getParent()->hasAttr<FinalAttr>())
----------------
haoNoQ wrote:

These are two different functions: `C.getLocationContext()->getDecl()` is the 
caller, `Call.getDecl()` is the callee.

It may actually be incorrect to check the caller function 
`C.getLocationContext()->getDecl()` because it's not necessarily our 
constructor/destructor. Could be an unrelated function invoked on a different 
object by our constructor/destructor, and that function may also be final in 
its own class.

Could also be, well, the constructor/destructor of our superclass. And in those 
constructors/destructors the vtable is actually already/still broken 
(https://godbolt.org/z/Enfoe95or). So in this situation we still want to emit 
the warning, and `C.getLocationContext()->getDecl()` would produce correct 
results.

(Also even if the entire class isn't `final`, the virtual method itself might 
still be `final`. And we don't actually care whether the constructor/destructor 
is final, we only care about the method. In this sense checking 
`C.getLocationContext()->getDecl()` is also somewhat counterproductive.)

And I'm actually not sure whether `Call.getDecl()` (aka `MD`) will show the 
base class method when we're in a base class constructor/destructor. I think it 
may be too smart for its own good. (Which may ultimately result in incorrect 
path simulation too, given that this checker isn't there to stop it. If that's 
a real problem, we'll ultimately need to address it by tearing down our dynamic 
type information early enough. Maybe it's already fine idk.) So I'm worried 
that it may look like it's calling the final override even when it's invoked 
from a base class constructor.

So I think this part may be incorrect either way, and it definitely needs to be 
covered by a few more tests to confirm that it's doing the right thing in 
presence of interprocedural analysis. (Try to call the method from inside the 
base class constructor/destructor, try to call the method after diving into an 
unrelated function.)

Given that the patch only reduces the amount of warnings we emit, I'm ok with 
landing it even with slight incorrectness. (@steakhal's version because it may 
actually be correct, plus check for the attribute on the method itself.) But 
looks like there's some interesting follow-up work here. And it's probably a 
good idea to add those tests anyway, even if we aren't immediately fixing them, 
just to have a rough idea how much we're missing.

https://github.com/llvm/llvm-project/pull/178654
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to