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);

Reply via email to