Hi! The OBJ_TYPE_REF constexpr handling looks through DECL_FIELD_IS_BASE COMPONENT_REFs to find the actual object on which the method is called, but as the following testcase shows, we need to do the similar thing also for the argument passed as this, because cxx_eval_indirect_ref otherwise fails to fold it. It can handle going from derived to base, not not vice versa.
Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-11-27 Jakub Jelinek <ja...@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. --- gcc/cp/constexpr.c.jj 2019-11-27 17:26:57.229014008 +0100 +++ gcc/cp/constexpr.c 2019-11-27 17:53:37.477566346 +0100 @@ -1441,6 +1441,24 @@ cxx_bind_parameters_in_call (const const arg = adjust_temp_type (type, arg); if (!TREE_CONSTANT (arg)) *non_constant_args = true; + /* For virtual calls, adjust the this argument, so that it is + the object on which the method is called, rather than + one of its bases. */ + 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); --- gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C.jj 2019-11-27 18:01:17.181532622 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-virtual14.C 2019-11-27 18:00:49.620954471 +0100 @@ -0,0 +1,27 @@ +// PR c++/92695 +// { dg-do compile { target c++2a } } + +struct A { + virtual int get () = 0; + 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 C { + B b; + A *c { addressof (b) }; + constexpr int add () { return c->set (addressof (b)); } +}; +struct D { + B b[2]; + A *c { addressof (b[0]) }; + constexpr int add () { return c->set (addressof (b[0])); } +}; +template <typename T> +constexpr int get () { T f; return f.add (); } +static_assert (get<C> () == 20); +static_assert (get<D> () == 20); Jakub