Hi! Since C++20 virtual methods can be constexpr, and if they are constexpr evaluated, we choose tentative_decl_linkage for those defer their output and decide at_eof again. On the following testcases we ICE though, because if expand_or_defer_fn_1 decides to use tentative_decl_linkage, it returns true and the caller in that case cals emit_associated_thunks, where use_thunk which it calls asserts DECL_INTERFACE_KNOWN on the thunk destination, which isn't the case for tentative_decl_linkage.
The following patch fixes the ICE by not emitting the thunks for the DECL_DEFER_OUTPUT fns just yet but waiting until at_eof time when we return to those. Note, the second testcase ICEs already since r0-110035 with -std=c++0x before it gets a chance to diagnose constexpr virtual method. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and eventually for backports? 2024-10-30 Jakub Jelinek <ja...@redhat.com> PR c++/117317 * semantics.cc (emit_associated_thunks): Do nothing for !DECL_INTERFACE_KNOWN && DECL_DEFER_OUTPUT fns. * g++.dg/cpp2a/pr117317-1.C: New test. * g++.dg/cpp2a/pr117317-2.C: New test. --- gcc/cp/semantics.cc.jj 2024-10-25 10:00:29.433768358 +0200 +++ gcc/cp/semantics.cc 2024-10-29 13:10:32.234068524 +0100 @@ -5150,7 +5150,10 @@ emit_associated_thunks (tree fn) enabling you to output all the thunks with the function itself. */ if (DECL_VIRTUAL_P (fn) /* Do not emit thunks for extern template instantiations. */ - && ! DECL_REALLY_EXTERN (fn)) + && ! DECL_REALLY_EXTERN (fn) + /* Do not emit thunks for tentative decls, those will be processed + again at_eof if really needed. */ + && (DECL_INTERFACE_KNOWN (fn) || !DECL_DEFER_OUTPUT (fn))) { tree thunk; --- gcc/testsuite/g++.dg/cpp2a/pr117317-1.C.jj 2024-10-29 13:12:23.373519669 +0100 +++ gcc/testsuite/g++.dg/cpp2a/pr117317-1.C 2024-10-29 13:12:18.223591437 +0100 @@ -0,0 +1,19 @@ +// PR c++/117317 +// { dg-do compile { target c++20 } } + +struct C { + constexpr bool operator== (const C &b) const { return foo (); } + constexpr virtual bool foo () const = 0; +}; +class A : public C {}; +class B : public C {}; +template <int> +struct D : A, B +{ + constexpr bool operator== (const D &) const = default; + constexpr bool foo () const override { return true; } +}; +struct E : D<1> {}; +constexpr E e; +constexpr E f; +static_assert (e == f, ""); --- gcc/testsuite/g++.dg/cpp2a/pr117317-2.C.jj 2024-10-29 13:16:10.101359947 +0100 +++ gcc/testsuite/g++.dg/cpp2a/pr117317-2.C 2024-10-29 13:16:15.981278003 +0100 @@ -0,0 +1,15 @@ +// PR c++/117317 +// { dg-do compile { target c++20 } } + +struct C { + constexpr virtual bool foo () const = 0; +}; +struct A : public C {}; +struct B : public C {}; +template <int> +struct D : A, B +{ + constexpr bool foo () const override { return true; } +}; +constexpr D<0> d; +static_assert (d.foo (), ""); Jakub