The following code fails with: -> error: expression 'A::get' is not a constant expression Compiled with `-std=c++2a` under GCC-9.2 and GCC-trunk. It works under Clang-trunk. ``` struct A { constexpr virtual int get() = 0; }; struct B : A { constexpr int get() override { return 10; } }; struct Foo { B b[1] = {}; constexpr A * get_a() { // Seems to be the problem. return &(b[0]); } }; constexpr int get() { Foo f; return f.get_a()->get(); } constexpr auto a = get(); int main() { return a; } ``` The dereferencing of the array seems to be the problem. See online here: https://gcc.godbolt.org/z/ZSXAfb
Future more, the following example also fails. Could be the same root cause but another error message appears: accessing value of 'f.Foo::b[0].B::<anonymous>' through a 'B' glvalue in a constant expression Clang is just fine with it. ``` struct A { constexpr virtual int get() = 0; constexpr virtual int set(A *o) = 0; }; struct B : A { constexpr int get() override { return 10; }struct A { constexpr virtual int get() = 0; constexpr virtual int set(A *o) = 0; }; struct B : A { constexpr int get() override { return 10; } constexpr int set(A *o) override { a = o; return 20; } A *a{}; }; constexpr auto addressof = [](A &n) { return &n; }; struct Foo { B b[1]; A* curr_{addressof(b[0])}; constexpr int add() { return curr_->set(addressof(b[0])); } }; constexpr int get() { Foo f; return f.add(); } constexpr auto a = get(); int main() { return a; } constexpr int set(A *o) override { a = o; return 20; } A *a{}; }; constexpr auto addressof = [](A &n) { return &n; }; struct Foo { B b[1]; A* curr_{addressof(b[0])}; constexpr int add() { return curr_->set(addressof(b[0])); } }; constexpr int get() { Foo f; return f.add(); } constexpr auto a = get(); int main() { return a; } ``` See online here: https://gcc.godbolt.org/z/4ZwFAN
Copy paste error. The above example should be: ``` struct A { constexpr virtual int get() = 0; constexpr virtual int set(A *o) = 0; }; struct B : A { constexpr int get() override { return 10; } constexpr int set(A *o) override { a = o; return 20; } A *a{}; }; constexpr auto addressof = [](A &n) { return &n; }; struct Foo { B b[1]; A* curr_{addressof(b[0])}; constexpr int add() { return curr_->set(addressof(b[0])); } }; constexpr int get() { Foo f; return f.add(); } constexpr auto a = get(); int main() { return a; } ```
Doesn't have to be an array, struct A { constexpr virtual int get () = 0; constexpr virtual int set (A *o) = 0; }; struct B : A { constexpr int get () override { return 10; } constexpr int set (A *o) override { a = o; return 20; } A *a{}; }; constexpr auto addressof = [] (A &n) { return &n; }; struct Foo { B b; A *c { addressof (b) }; constexpr int add () { return c->set (addressof (b)); } }; constexpr int get () { Foo f; return f.add (); } constexpr auto a = get (); static_assert (a == 20); is rejected too. I think the problem is that the r264408 testcase coverage doesn't try to dereference this arguments of the virtual methods, which is what happens in this testcase (the store to this->a in B::set(A *)). The OBJ_TYPE_REF handling in cxx_eval_constant_expression has: obj = TREE_OPERAND (obj, 0); while (TREE_CODE (obj) == COMPONENT_REF && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1))) obj = TREE_OPERAND (obj, 0); but something similar isn't done in cxx_eval_indirect_ref and I think generally shouldn't be done there, it is (is it?) only the special case of virtual method's first argument. So, I'd say this DECL_FIELD_IS_BASE handling should be done in cxx_eval_call_expression.
To be precise, I meant something like: --- gcc/cp/constexpr.c.jj 2019-11-27 10:03:37.916867165 +0100 +++ gcc/cp/constexpr.c 2019-11-27 16:55:17.475150697 +0100 @@ -1441,6 +1441,22 @@ cxx_bind_parameters_in_call (const const arg = adjust_temp_type (type, arg); if (!TREE_CONSTANT (arg)) *non_constant_args = true; + if (i == 0 + && DECL_VIRTUAL_P (fun)) + { + tree addr = arg; + STRIP_NOPS (addr); + if (TREE_CODE (addr) == ADDR_EXPR) + { + tree obj = TREE_OPERAND (addr, 0); + while (TREE_CODE (obj) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1))) + obj = TREE_OPERAND (obj, 0); + if (obj != TREE_OPERAND (addr, 0)) + arg = build_fold_addr_expr_with_type (obj, + TREE_TYPE (arg)); + } + } TREE_VEC_ELT (binds, i) = arg; } parms = TREE_CHAIN (parms); This fixes the testcase. Note, there is still a weird warning: pr92695.C:3:25: warning: inline function ‘virtual constexpr int A::set(A*)’ used but never defined 3 | constexpr virtual int set (A *o) = 0; | ^~~
Created attachment 47382 [details] gcc10-pr92695.patch Untested fix.
Created attachment 47383 [details] gcc10-pr92695.patch And here is a fix for the bogus warning. inline or constexpr on pure virtual functions looks useless, but the standard doesn't disallow that it seems.
First of all thank you very much for your extremly fast help! I testet the patch and it did work for my second example. But this one still fails, if we do not use the addressof function: struct A { virtual int get() = 0; }; struct B : A { constexpr int get() override { return 10; } }; struct D { B b[2]; A* c{&(b[0])}; }; static_assert(D{}.c->get() == 10); Error: main.cpp:20:28: error: non-constant condition for static assertion 20 | static_assert(D{}.c->get() == 10); | ~~~~~~~~~~~~~^~~~~ main.cpp:20:28: error: expression 'A::get' is not a constant expression
Created attachment 47384 [details] gcc10-pr92695-3.patch That is another bug.
Created attachment 47385 [details] gcc10-pr92695-2.patch Oops, the second patch I've attached was the same as the first one, instead of this one.
Author: jakub Date: Thu Nov 28 08:06:09 2019 New Revision: 278802 URL: https://gcc.gnu.org/viewcvs?rev=278802&root=gcc&view=rev Log: PR c++/92695 * decl2.c (mark_used): Don't call note_vague_linkage_fn for pure virtual functions, even if they are declared inline. * g++.dg/warn/inline3.C: New test. Added: trunk/gcc/testsuite/g++.dg/warn/inline3.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/decl2.c trunk/gcc/testsuite/ChangeLog
I tested all patches and it works as expected. Thank you very much!
Hello, I got another error using virtual classes: ``` struct A { virtual constexpr ~A() = default; }; struct B : A {}; constexpr bool test() { B b; return true; } static_assert(test()); ``` -> virtual constexpr B::~B()' used before its definition
For #c12, I think that is because of: 5615 /* Defer virtual destructors so that thunks get the right 5616 linkage. */ 5617 if (DECL_VIRTUAL_P (decl) && !at_eof) 5618 { 5619 note_vague_linkage_fn (decl); 5620 return true; 5621 } which makes mark_used not to synthesize those until end of TU. Not really sure how to fix that, shall we if constexpr synthesize them early anyway, but only register for constexpr evaluation and undo other effects of the synthesize_method? Or handle specially during constexpr evaluation each time we see those (or just first time, by remembering the body)?
Author: jakub Date: Mon Dec 2 21:33:06 2019 New Revision: 278912 URL: https://gcc.gnu.org/viewcvs?rev=278912&root=gcc&view=rev Log: PR c++/92695 * constexpr.c (cxx_eval_constant_expression) <case OBJ_TYPE_REF>: Use STRIP_NOPS before checking for ADDR_EXPR. * g++.dg/cpp2a/constexpr-virtual15.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual15.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/testsuite/ChangeLog
I really cannot help you with this. I am sorry. I don't understand the compilers source code/internals at all. I just can thank you guys for your ongoing work. I really appreciate it.
Author: jakub Date: Tue Dec 3 08:19:04 2019 New Revision: 278921 URL: https://gcc.gnu.org/viewcvs?rev=278921&root=gcc&view=rev Log: PR c++/92695 * constexpr.c (cxx_bind_parameters_in_call): For virtual calls, adjust the first argument to point to the derived object rather than its base. * g++.dg/cpp2a/constexpr-virtual14.C: New test. Added: trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/constexpr.c trunk/gcc/testsuite/ChangeLog
(In reply to Toni Neubert from comment #15) > I really cannot help you with this. I am sorry. I don't understand the > compilers source code/internals at all. I just can thank you guys for your > ongoing work. I really appreciate it. The question wasn't meant at you, but at the C++ maintainers. From my POV, all but #c12 should be fixed now (plus Jason mentioned some issue with multiple inheritance and constexpr virtual method call evaluation).
Author: jakub Date: Fri Dec 20 17:26:28 2019 New Revision: 279661 URL: https://gcc.gnu.org/viewcvs?rev=279661&root=gcc&view=rev Log: Backported from mainline 2019-11-28 Jakub Jelinek <jakub@redhat.com> PR c++/92695 * decl2.c (mark_used): Don't call note_vague_linkage_fn for pure virtual functions, even if they are declared inline. * g++.dg/warn/inline3.C: New test. Added: branches/gcc-9-branch/gcc/testsuite/g++.dg/warn/inline3.C Modified: branches/gcc-9-branch/gcc/cp/ChangeLog branches/gcc-9-branch/gcc/cp/decl2.c branches/gcc-9-branch/gcc/testsuite/ChangeLog
Author: jakub Date: Fri Dec 20 17:32:23 2019 New Revision: 279663 URL: https://gcc.gnu.org/viewcvs?rev=279663&root=gcc&view=rev Log: Backported from mainline 2019-12-02 Jakub Jelinek <jakub@redhat.com> PR c++/92695 * constexpr.c (cxx_eval_constant_expression) <case OBJ_TYPE_REF>: Use STRIP_NOPS before checking for ADDR_EXPR. * g++.dg/cpp2a/constexpr-virtual15.C: New test. Added: branches/gcc-9-branch/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual15.C Modified: branches/gcc-9-branch/gcc/cp/ChangeLog branches/gcc-9-branch/gcc/cp/constexpr.c branches/gcc-9-branch/gcc/testsuite/ChangeLog
Author: jakub Date: Fri Dec 20 17:33:13 2019 New Revision: 279664 URL: https://gcc.gnu.org/viewcvs?rev=279664&root=gcc&view=rev Log: Backported from mainline 2019-12-03 Jakub Jelinek <jakub@redhat.com> PR c++/92695 * constexpr.c (cxx_bind_parameters_in_call): For virtual calls, adjust the first argument to point to the derived object rather than its base. * g++.dg/cpp2a/constexpr-virtual14.C: New test. Added: branches/gcc-9-branch/gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C Modified: branches/gcc-9-branch/gcc/cp/ChangeLog branches/gcc-9-branch/gcc/cp/constexpr.c branches/gcc-9-branch/gcc/testsuite/ChangeLog
Fixed in 9.3/10.
Looks like the testcase in comment #12 was not resolved with the patches but is filed as PR 93413.