https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88335
--- Comment #14 from Jakub Jelinek <jakub at gcc dot gnu.org> --- I think we should remove the __cpp_consteval define until we implement virtual consteval and should also mention in cxx-status.html that it is only partially implemented. I've tried to play with the virtual consteval support, but am stuck. First, some testcases I've been playing with: One for diagnostics: struct S { virtual int foo () { return 42; } // { dg-message "overridden function is 'virtual consteval int S::foo\\\(\\\)'" } consteval virtual int bar () { return 43; } // { dg-message "overridden function is 'virtual consteval int S::bar\\\(\\\)'" } }; struct T : public S { int bar () { return 44; } // { dg-error "non-'consteval' function 'virtual int T::bar\\\(\\\)' overriding 'consteval' function" } }; struct U : public S { consteval virtual int foo () { return 45; } // { dg-error "'consteval' function 'virtual consteval int U::foo\\\(\\\)' overriding non-'consteval' function" } }; And the main one: struct S { constexpr S () : s (0) {} virtual int foo () const { return 42; } consteval virtual int bar () const { return 43; } consteval virtual int baz () const { return 44; } int s; }; struct T : public S { constexpr T () : t (0) {} consteval int bar () const { return 45; } consteval virtual int baz () const { return 46; } int t; }; struct U : public T { typedef int bar; typedef int baz; }; consteval int foo () { S s; T t; U u; S *v = (S *) &t; S *w = (S *) &u; if (s.bar () != 43) throw 1; if (s.baz () != 44) throw 2; if (t.bar () != 45) throw 3; if (t.baz () != 46) throw 4; if (v->bar () != 45) throw 5; if (v->baz () != 46) throw 6; if (w->bar () != 45) throw 7; if (w->baz () != 46) throw 8; if (t.S::bar () != 43) throw 9; if (t.T::baz () != 46) throw 10; if (v->S::bar () != 43) throw 11; if (w->S::baz () != 44) throw 12; return 0; } constexpr S s; constexpr T t; constexpr const S * bar (bool x) { return x ? &s : (const S *) &t; } int a = foo (); int b = bar (false)->bar (); int c = bar (true)->baz (); static_assert (bar (false)->bar () == 45); static_assert (bar (true)->baz () == 44); Now, the issues: 1) (so far ignored); the standard says that classes where all virtual members are immediate are still polymorphic, but I guess for the ABI we don't want a vtable pointer there. So, I think we want TYPE_POLYMORPHIC_P set on those, but e.g. TYPE_CONTAINS_VPTR_P probably shouldn't be true for them; do we want TYPE_REALLY_POLYMORPHIC_P or similar for polymorphic types that contain at least one non-immediate virtual function and thus need a vtable? 2) initially I thought I'd just always emit a direct call to the immediate virtual method found by lookup and do the remapping of that during constexpr call evaluation; unfortunately as the v->S::bar () etc. calls show, we only want to do that if LOOKUP_NONVIRTUAL wasn't set; unfortunately, when immediate functions aren't in the binfo structures, DECL_VINDEX is error_mark_node and so I think we need some hack how to preserve the info that we are going to call a virtual consteval method; could we e.g. abuse OBJ_TYPE_REF with different arguments that would make it clear it is something different, or new tree? We need to store the instance on which it is called and the virtual consteval method originally chosen e.g. to compare the type 3) I'm afraid one can't use a lookup_member on the actual instance type, because it could find all kinds of things, static member functions, typedefs, data members etc. in derived classes, where we actually are only interested in in virtual methods. So, shall we use something like look_for_overrides does, except with the fndecl from the base rather than derived and of course don't do anything except return the first found method (and ignore static member functions rather than handling them)? 4) guess covariant returns need to be handled at the end too somehow Current WIP patch (though as mentioned in 2), in build_over_call we probably just need some way note that it needs to be a virtual consteval call and evaluate that only during constexpr evaluation): --- gcc/cp/call.c.jj 2019-11-28 09:02:26.953819534 +0100 +++ gcc/cp/call.c 2019-11-28 18:18:31.646444362 +0100 @@ -8369,6 +8369,7 @@ build_over_call (struct z_candidate *can current_function_returns_abnormally = 1; if (TREE_CODE (fn) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (fn) + && !DECL_VINDEX (fn) && (current_function_decl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) && (current_binding_level->kind != sk_function_parms @@ -8962,7 +8963,40 @@ build_over_call (struct z_candidate *can && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL) maybe_warn_class_memaccess (input_location, fn, args); - if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0) + if (!DECL_VINDEX (fn) + || (flags & LOOKUP_NONVIRTUAL) != 0) + { + fn = build_addr_func (fn, complain); + if (fn == error_mark_node) + return error_mark_node; + } + else if (DECL_IMMEDIATE_FUNCTION_P (fn)) + { + tree obj = cxx_constant_value (argarray[0]); + if (obj == error_mark_node) + return error_mark_node; + STRIP_NOPS (obj); + if (TREE_CODE (obj) != ADDR_EXPR + || !DECL_P (get_base_address (TREE_OPERAND (obj, 0)))) + { + if (complain & tf_error) + error ("invalid call to %<consteval%> %<virtual%> function %qD", + fn); + return error_mark_node; + } + obj = TREE_OPERAND (obj, 0); + while (TREE_CODE (obj) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1))) + obj = TREE_OPERAND (obj, 0); + tree objtype = TREE_TYPE (obj); +/* FIXME: I think we can't use lookup_member, as the virtual fn could be + hidden by types/data members etc. in derived classes. + tree member = lookup_member (objtype, DECL_NAME (fn), 0, */ + fn = build_addr_func (fn, complain); + if (fn == error_mark_node) + return error_mark_node; + } + else { tree t; tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (argarray[0])), @@ -8978,12 +9012,6 @@ build_over_call (struct z_candidate *can fn = build_vfn_ref (argarray[0], DECL_VINDEX (fn)); TREE_TYPE (fn) = t; } - else - { - fn = build_addr_func (fn, complain); - if (fn == error_mark_node) - return error_mark_node; - } tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag); if (call == error_mark_node) --- gcc/cp/class.c.jj 2019-11-26 22:56:37.862289986 +0100 +++ gcc/cp/class.c 2019-11-28 15:18:26.222237721 +0100 @@ -6002,7 +6002,9 @@ create_vtable_ptr (tree t, tree* virtual /* Collect the virtual functions declared in T. */ for (fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn)) if (TREE_CODE (fn) == FUNCTION_DECL - && DECL_VINDEX (fn) && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn) + && DECL_VINDEX (fn) + && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn) + && !DECL_IMMEDIATE_FUNCTION_P (fn) && TREE_CODE (DECL_VINDEX (fn)) != INTEGER_CST) { tree new_virtual = make_node (TREE_LIST); --- gcc/cp/search.c.jj 2019-10-10 01:33:38.286941969 +0200 +++ gcc/cp/search.c 2019-11-28 15:53:17.919926566 +0100 @@ -1972,20 +1972,13 @@ check_final_overrider (tree overrider, t /* OK */; else { + auto_diagnostic_group d; if (fail == 1) - { - auto_diagnostic_group d; - error ("invalid covariant return type for %q+#D", overrider); - inform (DECL_SOURCE_LOCATION (basefn), - "overridden function is %q#D", basefn); - } + error ("invalid covariant return type for %q+#D", overrider); else - { - auto_diagnostic_group d; - error ("conflicting return type specified for %q+#D", overrider); - inform (DECL_SOURCE_LOCATION (basefn), - "overridden function is %q#D", basefn); - } + error ("conflicting return type specified for %q+#D", overrider); + inform (DECL_SOURCE_LOCATION (basefn), + "overridden function is %q#D", basefn); DECL_INVALID_OVERRIDER_P (overrider) = 1; return 0; } @@ -2006,6 +1999,22 @@ check_final_overrider (tree overrider, t DECL_INVALID_OVERRIDER_P (overrider) = 1; return 0; } + + if (DECL_IMMEDIATE_FUNCTION_P (overrider) + != DECL_IMMEDIATE_FUNCTION_P (basefn)) + { + auto_diagnostic_group d; + if (DECL_IMMEDIATE_FUNCTION_P (overrider)) + error ("%<consteval%> function %q+D overriding non-%<consteval%> " + "function", overrider); + else + error ("non-%<consteval%> function %q+D overriding %<consteval%> " + "function", overrider); + inform (DECL_SOURCE_LOCATION (basefn), + "overridden function is %qD", basefn); + DECL_INVALID_OVERRIDER_P (overrider) = 1; + return 0; + } /* A function declared transaction_safe_dynamic that overrides a function declared transaction_safe (but not transaction_safe_dynamic) is --- gcc/cp/decl.c.jj 2019-11-28 13:09:24.748799556 +0100 +++ gcc/cp/decl.c 2019-11-28 15:31:20.675276991 +0100 @@ -9384,15 +9384,6 @@ grokfndecl (tree ctype, } } - /* FIXME: For now. */ - if (virtualp && (inlinep & 8) != 0) - { - sorry_at (DECL_SOURCE_LOCATION (decl), - "%<virtual%> %<consteval%> method %qD not supported yet", - decl); - inlinep &= ~8; - } - /* If this decl has namespace scope, set that up. */ if (in_namespace) set_decl_namespace (decl, in_namespace, friendp);