llvm / llvm-project

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

clang incorrectly emits `-Wundefined-func-template` warning #92486

Open ahatanak opened 2 months ago

ahatanak commented 2 months ago

$ cat test.cpp

class c {
protected:
  virtual ~c();
  virtual int *d() const;
};

template <typename> class e : c {
  int *d() const override;
};

int t = sizeof(e<int>);

$ clang++ -c -std=gnu++2b -Wundefined-func-template test.cpp

test.cpp:7:27: warning: instantiation of function 'e::d' required here, but no definition is available [-Wundefined-func-template] 7 | template class e : c {

I don’t think instantiation of e::d is needed to compute the size of the class.

llvmbot commented 2 months ago

@llvm/issue-subscribers-clang-frontend

Author: Akira Hatanaka (ahatanak)

$ cat test.cpp ``` class c { protected: virtual ~c(); virtual int *d() const; }; template <typename> class e : c { int *d() const override; }; int t = sizeof(e<int>); ``` $ clang++ -c -std=gnu++2b -Wundefined-func-template test.cpp test.cpp:7:27: warning: instantiation of function 'e<int>::d' required here, but no definition is available [-Wundefined-func-template] 7 | template <typename> class e : c { I don’t think instantiation of e<int>::d is needed to compute the size of the class.
ahatanak commented 2 months ago

It looks like clang started emitting the warning after 99500e8c08a4d941acb8a7eb00523296fb2acf7a. clang doesn't warn if I revert the commit.

shafik commented 2 months ago

CC @Fznamznon

MitalAshok commented 2 months ago

This also happens in Clang 18 if we have https://godbolt.org/z/7GzabWKxq

class c {
protected:
  virtual ~c();
  virtual int d() const;
};

template <typename> class e : c {
  constexpr ~e() = default;
  int d() const override;
};

int t = sizeof(e<int>);

This appears to have just been exposed when the implicitly-declared destructor became constexpr via https://wg21.link/p2448r2#pnum_23

zygoloid commented 2 months ago

I think this is ill-formed NDR -- the class is required to be complete, so its virtual functions are ODR-used.

In practice, e<int> has a constexpr virtual destructor, so we instantiate it eagerly, and we instantiate the complete vtable with the destructor, so the behavior here makes some sense. But it would seem better if we could defer marking the vtable used until the destructor is actually used (not just instantiated because it might be used at compile time, which doesn't need the vtable).