Bug 88335 - Implement P1073R3, C++20 immediate functions (consteval).
Summary: Implement P1073R3, C++20 immediate functions (consteval).
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.0
: P3 enhancement
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks: c++20-core
  Show dependency treegraph
 
Reported: 2018-12-03 19:14 UTC by Ed Smith-Rowland
Modified: 2020-09-25 23:59 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-05-20 00:00:00


Attachments
gcc10-pr88335.patch (4.55 KB, patch)
2019-05-20 18:43 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335-wip.patch (7.46 KB, patch)
2019-05-21 12:20 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335-wip.patch (8.53 KB, patch)
2019-05-29 12:58 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335-wip.patch (8.08 KB, patch)
2019-10-14 15:42 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335-wip.patch (8.40 KB, patch)
2019-10-14 16:45 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335.patch (12.22 KB, patch)
2019-10-15 15:06 UTC, Jakub Jelinek
Details | Diff
gcc10-pr88335.patch (12.62 KB, text/plain)
2019-10-23 13:53 UTC, Jakub Jelinek
Details
gcc10-wip-consteval-virtual.patch (4.28 KB, patch)
2019-12-16 15:52 UTC, Jakub Jelinek
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ed Smith-Rowland 2018-12-03 19:14:05 UTC

    
Comment 1 Jakub Jelinek 2019-05-20 18:43:18 UTC
Created attachment 46388 [details]
gcc10-pr88335.patch

Untested WIP patch.  Need to decide now from where exactly to call the immediate functions when not in immediate function contexts, shall that be done say from build_call_a/build_over_call once the expression is built and if nothing in the expression is dependent, or shall it be done say only from cp_fold?  The latter seems better conceptually (that we don't fold stuff too early), but might need to handle it also in various unevaluated codepaths.
Also, I guess if during !ctx->manifestly_const_eval constexpr evaluation we encounter immediate function call, we need to evaluate the call separately in a manifestly_const_eval mode and only then return back to the normal constexpr evaluation.
Some work will need to be done to make sure we don't hand over immediate functions to the middle-end, another question is what to do about virtual immediate functions, for the constexpr evaluations we want to see them in whatever data structures we have for virtual tables, but what we later hand over to the middle-end and emit should not include those, what shall we do with
classes that don't have any virtual members other than immediate functions, etc.
Comment 2 Jakub Jelinek 2019-05-20 18:44:03 UTC
Jason, thoughts on this?
Comment 3 Jakub Jelinek 2019-05-21 12:20:11 UTC
Created attachment 46390 [details]
gcc10-pr88335-wip.patch

Updated patch.  This one tries to evaluate immediate function calls immediately in build_over_call, adds some testsuite coverage etc.
What still doesn't work is:
1) consteval constructors, I think evaluating those in build_over_call is too early, we need a TARGET_EXPR for them or something similar that supplies to the constexpr evaluation the object that is being initialized; so, where should we do that and should we do that that way only for ctors, or everything?
2) the example added to the end of [expr.const] doesn't work - we have in cxx_eval_outermost_constant_expr:
  if (VOID_TYPE_P (type))
    return t;
early exit.  Is that something that is correct for anything?  I mean, aren't we supposed to evaluate the constexpr (and consteval) functions anyway?
constexpr void foo (bool x) { if (x) throw 1; }
constexpr int a = (foo (false), 1);
constexpr int b = (foo (true), 2);
is handled correctly though, because in that case the void type expressions are the outermost ones.  Perhaps we should do this early exit only if the expression isn't a call to an immediate function?
3) consteval virtual members not handled at all, and I'm afraid that is out of my area of expertise
Comment 4 Jakub Jelinek 2019-05-21 13:12:52 UTC
Not working on this anymore.
Comment 5 Jakub Jelinek 2019-05-29 12:58:21 UTC
Created attachment 46429 [details]
gcc10-pr88335-wip.patch

Slightly updated patch.
Comment 6 Jakub Jelinek 2019-05-29 13:05:24 UTC
The above patch:
1) adds sorry_at for virtual consteval, that is quite a lot of work
2) still doesn't handle ctors properly (perhaps sorry_at too)?
3) fixes the testcase from the paper with decltype containing call to void returning consteval fn
4) adds diagnostics for immediate invocation returning address of some immediate function
Unfortunately 4) breaks consteval2.C testcase, my reading of the spec is we should treat default arguments of immediate functions as in immediate function context and not evaluate immediately, but current_function_decl at that point doesn't indicate we are in immediate function context, and I guess we don't even have such a decl.  Jason, any thoughts on that?
Comment 7 Toni Neubert 2019-10-14 14:11:53 UTC
Any progress on this? How can we help?
Comment 8 Marek Polacek 2019-10-14 15:40:31 UTC
Work has been restarted, we should have this feature fairly soon.  Certainly the plan is to have it in GCC 10.
Comment 9 Jakub Jelinek 2019-10-14 15:42:42 UTC
Created attachment 47031 [details]
gcc10-pr88335-wip.patch

As the first step, here is the previous patch updated so that it applies to current trunk (various changes due to constinit etc.), no further functional changes so far.
Comment 10 Jakub Jelinek 2019-10-14 16:45:54 UTC
Created attachment 47032 [details]
gcc10-pr88335-wip.patch

Small improvements and one bugfix.
Comment 11 Jakub Jelinek 2019-10-15 15:06:01 UTC
Created attachment 47042 [details]
gcc10-pr88335.patch

Untested patch.
Comment 12 Jakub Jelinek 2019-10-23 13:53:51 UTC
Created attachment 47094 [details]
gcc10-pr88335.patch

Just for the record, this is a variant of the patch that has testsuite coverage for immediate function calls in discarded statements (at least how I believe it should work), still using the cxx_eval_consteval rather than using again build_cxx_call.
Comment 13 Jakub Jelinek 2019-11-01 23:28:51 UTC
Author: jakub
Date: Fri Nov  1 23:28:20 2019
New Revision: 277733

URL: https://gcc.gnu.org/viewcvs?rev=277733&root=gcc&view=rev
Log:
	PR c++/88335 - Implement P1073R3: Immediate functions
c-family/
	* c-common.h (enum rid): Add RID_CONSTEVAL.
	* c-common.c (c_common_reswords): Add consteval.
cp/
	* cp-tree.h (struct lang_decl_fn): Add immediate_fn_p bit.
	(DECL_IMMEDIATE_FUNCTION_P, SET_DECL_IMMEDIATE_FUNCTION_P): Define.
	(enum cp_decl_spec): Add ds_consteval.
	(fold_non_dependent_expr): Add another tree argument defaulted to
	NULL_TREE.
	* name-lookup.h (struct cp_binding_level): Add immediate_fn_ctx_p
	member.
	* parser.c (cp_keyword_starts_decl_specifier_p): Adjust comments
	for C++11 and C++20 specifiers.  Handle RID_CONSTEVAL.
	(CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): Adjust comment.
	(CP_PARSER_FLAGS_CONSTEVAL): New.
	(cp_parser_skip_balanced_tokens): New forward declaration.
	(cp_parser_lambda_declarator_opt): Handle ds_consteval.  Set
	current_binding_level->immediate_fn_ctx_p before parsing parameter
	list if decl-specifier-seq contains consteval specifier.
	(cp_parser_decl_specifier_seq): Handle RID_CONSTEVAL.
	(cp_parser_explicit_instantiation): Diagnose explicit instantiation
	with consteval specifier.
	(cp_parser_init_declarator): For consteval or into flags
	CP_PARSER_FLAGS_CONSTEVAL.
	(cp_parser_direct_declarator): If CP_PARSER_FLAGS_CONSTEVAL, set
	current_binding_level->immediate_fn_ctx_p in the sk_function_parms
	scope.
	(set_and_check_decl_spec_loc): Add consteval entry, formatting fix.
	* call.c (build_addr_func): For direct calls to immediate functions
	use build_address rather than decay_conversion.
	(build_over_call): Evaluate immediate function invocations.
	* error.c (dump_function_decl): Handle DECL_IMMEDIATE_FUNCTION_P.
	* semantics.c (expand_or_defer_fn_1): Use tentative linkage and don't
	call mark_needed for immediate functions.
	* typeck.c (cxx_sizeof_or_alignof_expr): Likewise.  Formatting fix.
	(cp_build_addr_expr_1): Reject taking address of immediate function
	outside of immediate function.
	* decl.c (validate_constexpr_redeclaration): Diagnose consteval
	vs. non-consteval or vice versa redeclaration.  Use
	SET_DECL_IMMEDIATE_FUNCTION_P if new_decl is immediate function.
	(check_tag_decl): Use %qs with keyword string to simplify translation.
	Handle ds_consteval.
	(start_decl): Adjust diagnostics for static or thread_local variables
	in immediate functions.
	(grokfndecl): Call sorry_at on virtual consteval.  Use %qs with keyword
	to string to simplify translation.  Diagnose consteval main.  Use
	SET_DECL_IMMEDIATE_FUNCTION_P for consteval.
	(grokdeclarator): Handle consteval.  Use %qs with keyword strings to
	simplify translation.  Use separate ifs instead of chained else if
	for invalid specifiers.  For constinit clear constinit_p rather than
	constexpr_p.
	* constexpr.c (find_immediate_fndecl): New function.
	(cxx_eval_outermost_constant_expr): Allow consteval calls returning
	void.  Diagnose returning address of immediate function from consteval
	evaluation.
	(fold_non_dependent_expr_template): Add OBJECT argument, pass it
	through to cxx_eval_outermost_constant_expr.
	(fold_non_dependent_expr): Add OBJECT argument, pass it through to
	fold_non_dependent_expr_template.
	(fold_non_dependent_init): Adjust fold_non_dependent_expr_template
	caller.
	* method.c (defaulted_late_check): Adjust diagnostics for consteval.
	* lambda.c (maybe_add_lambda_conv_op): Copy over
	DECL_DECLARED_CONSTEXPR_P and DECL_IMMEDIATE_FUNCTION_P bits from
	callop to both artificial functions.
	* init.c (build_value_init): Don't do further processing if
	build_special_member_call returned a TREE_CONSTANT.  Formatting fix.
testsuite/
	* g++.dg/cpp2a/consteval1.C: New test.
	* g++.dg/cpp2a/consteval2.C: New test.
	* g++.dg/cpp2a/consteval3.C: New test.
	* g++.dg/cpp2a/consteval4.C: New test.
	* g++.dg/cpp2a/consteval5.C: New test.
	* g++.dg/cpp2a/consteval6.C: New test.
	* g++.dg/cpp2a/consteval7.C: New test.
	* g++.dg/cpp2a/consteval8.C: New test.
	* g++.dg/cpp2a/consteval9.C: New test.
	* g++.dg/cpp2a/consteval10.C: New test.
	* g++.dg/cpp2a/consteval11.C: New test.
	* g++.dg/cpp2a/consteval12.C: New test.
	* g++.dg/cpp2a/consteval13.C: New test.
	* g++.dg/cpp2a/consteval14.C: New test.
	* g++.dg/ext/consteval1.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval1.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval10.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval11.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval12.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval13.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval14.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval2.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval3.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval4.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval5.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval6.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval7.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval8.C
    trunk/gcc/testsuite/g++.dg/cpp2a/consteval9.C
    trunk/gcc/testsuite/g++.dg/ext/consteval1.C
Modified:
    trunk/gcc/c-family/ChangeLog
    trunk/gcc/c-family/c-common.c
    trunk/gcc/c-family/c-common.h
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/cp/constexpr.c
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/cp/decl.c
    trunk/gcc/cp/error.c
    trunk/gcc/cp/init.c
    trunk/gcc/cp/lambda.c
    trunk/gcc/cp/method.c
    trunk/gcc/cp/name-lookup.h
    trunk/gcc/cp/parser.c
    trunk/gcc/cp/semantics.c
    trunk/gcc/cp/typeck.c
    trunk/gcc/testsuite/ChangeLog
Comment 14 Jakub Jelinek 2019-11-28 17:58:11 UTC
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);
Comment 15 Jason Merrill 2019-12-13 05:46:56 UTC
On 11/28/19 12:58 PM, jakub at gcc dot gnu.org wrote:
> 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?

We still need a vtable for typeinfo, so I think we don't need to worry 
about this.

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

Would it work to include them at the end of BINFO_VIRTUALS but omit them 
in build_vtbl_initializer?

> 4) guess covariant returns need to be handled at the end too somehow

Yep.

Jason
Comment 16 Jakub Jelinek 2019-12-13 12:23:46 UTC
(In reply to Jason Merrill from comment #15)
> Would it work to include them at the end of BINFO_VIRTUALS but omit them 
> in build_vtbl_initializer?

With my very limited understanding, I thought that for the vtables it is important that if a base defines some virtual method that for a derived type which either doesn't override it or overrides it the same virtual method has the same index.  Now, if the consteval virtuals are at the end of BINFO_VIRTUALS but indexed from the start as others, if say base has 3 normal and 2 consteval virtual methods, 0/1/2 indexes will be the normal and 3/4 will be the consteval ones.  If I derive a class from this, don't override the first normal virtual method, override the second one (dtor, two entries), don't override the first consteval method, override the second one and add one new normal and one new consteval methods, like:
struct base {
  virtual int foo ();
  virtual ~base ();
  virtual consteval int bar () { return 42; }
  virtual consteval int baz () { return 43; }
};
struct derived : public base {
  virtual ~derived ();
  virtual consteval int baz () { return 44; }
  virtual int qux ();
  virtual consteval int quux () { return 45; }
};
what would happen with indexes in derived BINFO_VIRTUALS?  If the non-consteval ones need to go first as that is what we emit into vtables, then qux needs to have index 3, but doesn't bar already have that?

The only thing that comes to my mind is to use  e.g. negative indexes for the consteval methods and count in that case from the end, i.e. foo would get DECL_VINDEX 0, dtors DECL_VINDEX 1/2, bar DECL_VINDEX -1, baz DECL_VINDEX -2,
qux DECL_VINDEX 3 and quux DECL_VINDEX -3 and when actually looking up the entry in BINFO_VIRTUALS, transform negative indexes into chain_length (BINFO_VIRTUALS) + DECL_VINDEX, i.e. bar would be the last in BINFO_VIRTUALS chain of both base and derived, baz the penultimate and quux in derived the antepenultimate.
Thoughts on that?
Comment 17 Jakub Jelinek 2019-12-16 15:52:53 UTC
Created attachment 47506 [details]
gcc10-wip-consteval-virtual.patch

I've tried to make further progress on this, and while simple testcases like those included in the patch work, consteval pmf for consteval virtual methods doesn't work and covariants don't work either and I'm stuck.
Comment 18 Jakub Jelinek 2019-12-16 15:59:44 UTC
Testcase for pmf (incomplete, guess the non-pmf calls should be converted too):
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;
};

consteval int
foo ()
{
  S s;
  T t;
  S *u = (S *) &t;
  T *v = &t;
  auto pmf1 = &S::bar;
  auto pmf2 = &S::baz;
  if ((s.*pmf1) () != 43) throw 1;
  if ((s.*pmf2) () != 44) throw 2;
  if ((t.*pmf1) () != 45) throw 3;
  if ((t.*pmf2) () != 46) throw 4;
  if ((u->*pmf1) () != 45) throw 5;
  if ((u->*pmf2) () != 46) throw 6;
  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);

Not sure what to do there, expand_ptrmemfunc_cst uses DECL_VINDEX which for immediate virtual functions is negative.  It could transform those into positive say by adding list_length (BINFO_VIRTUALS ()) to it, but during the constexpr evaluation it isn't evaluated as some kind of OBJ_TYPE_REF, but rather dereference of the vtable.  And, for vtable decls we don't want the consteval methods in there, so maybe cxx_eval_constant_call would need to figure out it is a PMF call to a consteval method and look it up in BINFO_VIRTUALS chain rather than actually in the vtable.

Testcase for covariants, this one compiles, but fails the assertion.  It is covariant2.C where I had to s/virtual/public/ on the D prototype, so that D's ctor could be constexpr, so maybe it is ok it works this way.
// { dg-do compile { target c++2a } }

struct B1;
struct B2;
struct D;

struct B1
{
  virtual consteval const B1 *foo1 () const {return this;}
  consteval virtual const B2 *foo2 (const D *) const;
};
struct B2
{
  consteval virtual const B2 *baz1 () const {return this;}
  consteval virtual const B1 *baz2 (const D *) const;
};

struct D : public B1, B2
{
  virtual consteval const D *foo1 () const {return this;}
  virtual consteval const D *foo2 (const D *d) const {return d;}
  consteval virtual const D *baz1 () const {return this;}
  virtual consteval const D *baz2 (const D *d) const {return d;}
};

consteval const B2 *B1::foo2 (const D *d) const {return d;}
consteval const B1 *B2::baz2 (const D *d) const {return d;}

consteval int
test (const B1 *b1, const B2 *b2, const D *d)
{
  if (b1->foo1 () != b1)
    return 1;
  if (b2->baz1 () != b2)
    return 2;
  if (b1->foo2 (d) != b2)
    return 3;
  if (b2->baz2 (d) != b1)
    return 4;
  return 0;
}

consteval int
test (const D *d)
{
  if (d->foo2 (d) != d)
    return 11;
  if (d->baz2 (d) != d)
    return 12;
  if (d->foo1 () != d)
    return 13;
  if (d->baz1 () != d)
    return 14;
  return 0;
}

constexpr D d;
constexpr auto e = test (&d, &d, &d);
constexpr auto f = test (&d);
//static_assert (e == 0);
static_assert (f == 0);
int g = e;
Comment 19 Jakub Jelinek 2019-12-16 16:03:30 UTC
Also note the update_vtable_entry_for_fn changes (except the formatting one) were done just in desperate attempt to avoid various ICEs, I'm afraid I have no idea what would need to be done to diagnose consteval method covariant returns are valid, but without ICEing because we can't find those in structures that don't have those already, or e.g. that we don't try to create thunks.  And, guess the cxx_eval_constant_call or somewhere similar it needs to be able to deal with what thunks would normally deal with.
Comment 20 Jakub Jelinek 2019-12-16 16:14:40 UTC
Not working on this further.
Comment 21 Marek Polacek 2020-09-25 23:59:00 UTC
Implemented in GCC 11.