Hi, these two PRs are about C++ destructors that are not virtual but have virtual alias. The devirtualization machinery walks from alias to symbol and is then confused by not knowing the class symbol belongs to.
I think it would make more sense to avoid these walks but that require more of surgery, so for GCC9 I think it is best to stop freeing contexts for desctructors. This does not save any stremaing because THIS pointer of the destructor has the same type. Bootstrapped/regtested x86_64-linux, OK? Honza PR lto/87809 PR lto/89335 * tree.c (free_lang_data_in_decl): Do not free context of C++ destrutors. * g++.dg/lto/pr87089_0.C: New testcase. * g++.dg/lto/pr87089_1.C: New testcase. * g++.dg/lto/pr89335_0.C: New testcase. Index: tree.c =================================================================== --- tree.c (revision 269704) +++ tree.c (working copy) @@ -5772,10 +5772,16 @@ free_lang_data_in_decl (tree decl, struc not do well with TREE_CHAIN pointers linking them. Also do not drop containing types for virtual methods and tables because - these are needed by devirtualization. */ + these are needed by devirtualization. + C++ destructors are special because C++ frontends sometimes produces + virtual destructor as an alias of non-virtual destructor. In + devirutalization code we always walk through aliases and we need + context to be preserved too. See PR89335 */ if (TREE_CODE (decl) != FIELD_DECL && ((TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != FUNCTION_DECL) - || !DECL_VIRTUAL_P (decl))) + || (!DECL_VIRTUAL_P (decl) + && (TREE_CODE (decl) != FUNCTION_DECL + || !DECL_CXX_DESTRUCTOR_P (decl))))) DECL_CONTEXT (decl) = fld_decl_context (DECL_CONTEXT (decl)); } Index: testsuite/g++.dg/lto/pr87089_0.C =================================================================== --- testsuite/g++.dg/lto/pr87089_0.C (nonexistent) +++ testsuite/g++.dg/lto/pr87089_0.C (working copy) @@ -0,0 +1,21 @@ +// { dg-lto-do link } +// { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" } +namespace itpp { +template <class a> void b(a *c) { c[0].~a(); } +class CFix; +template <class> class d { + void e(const char *); + CFix *data; +}; +class CFix { +public: + virtual ~CFix(); +}; +template <> void d<int>::e(const char *) { b(data); } +} // namespace itpp + +int +main (void) +{ + return 0; +} Index: testsuite/g++.dg/lto/pr87089_1.C =================================================================== --- testsuite/g++.dg/lto/pr87089_1.C (nonexistent) +++ testsuite/g++.dg/lto/pr87089_1.C (working copy) @@ -0,0 +1,12 @@ +namespace itpp { +enum a { b }; +class CFix { +public: + virtual ~CFix(); +}; +template <a = b> class c : CFix { + ~c() {} +}; +template class c<>; +} // namespace itpp + Index: testsuite/g++.dg/lto/pr89335_0.C =================================================================== --- testsuite/g++.dg/lto/pr89335_0.C (nonexistent) +++ testsuite/g++.dg/lto/pr89335_0.C (working copy) @@ -0,0 +1,16 @@ +// { dg-lto-do link } +// { dg-lto-options {{-O2 -flto -Wsuggest-final-methods}} } +// { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" } +class Container +{ +public: + virtual ~Container (); +}; +class List : public Container // { dg-lto-message "final would enable devirtualization" } +{ +}; +static List cache[256]; +int main (void) +{ + return 0; +}