public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-4730] c++: Diagnose taking address of an immediate member function [PR102753]
@ 2021-10-27  7:28 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2021-10-27  7:28 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:4b2fda8bea3fdcc9421726e5a21e537f745cad0b

commit r12-4730-g4b2fda8bea3fdcc9421726e5a21e537f745cad0b
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Wed Oct 27 09:03:28 2021 +0200

    c++: Diagnose taking address of an immediate member function [PR102753]
    
    The consteval20.C testcase ICEs, because while we have in cp_build_addr_expr_1
    diagnostics for taking address of an immediate function (and as an exception
    deal with build_address from immediate invocation), I forgot to diagnose
    taking address of a member function which is done in a different place.
    I hope (s.*&S::foo) () is not an immediate invocation like
    (*&foo) () is not, so this patch just diagnoses taking address of a member
    function when not in immediate context.
    
    On Mon, Oct 18, 2021 at 12:42:00PM -0400, Jason Merrill wrote:
    > > --- gcc/cp/typeck.c.jj      2021-10-05 09:53:55.382734051 +0200
    > > +++ gcc/cp/typeck.c 2021-10-15 19:28:38.034213437 +0200
    > > @@ -6773,9 +6773,21 @@ cp_build_addr_expr_1 (tree arg, bool str
    > >         return error_mark_node;
    > >       }
    > > +   if (TREE_CODE (t) == FUNCTION_DECL
    > > +       && DECL_IMMEDIATE_FUNCTION_P (t)
    > > +       && cp_unevaluated_operand == 0
    > > +       && (current_function_decl == NULL_TREE
    > > +           || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
    >
    > This doesn't cover some of the other cases of immediate context; we should
    > probably factor most of immediate_invocation_p out into a function called
    > something like in_immediate_context and use it here, and in several other
    > places as well.
    
    You're right, I've done that for the two spots in cp_build_addr_expr_1
    and added testsuite coverage for where it changed behavior.
    While doing that I've discovered further issues.
    
    One is that we weren't diagnosing PMFs referring to immediate methods
    returned from immediate functions (either directly or embedded in
    aggregates).  I'm not sure if it can only appear as PTRMEM_CST which
    I've handled (cp_walk_subtree only walks the type and not the
    PTRMEM_CST_MEMBER) or something else.
    
    Another issue is that while default arg in immediate function
    containing &immediate_fn works properly, if it is immediate_fn
    instead, we were incorrectly rejecting it.
    I've handled this in build_over_call, though with this usage
    in_consteval_if_p is slightly misnamed, it stands for in consteval
    if or some other reason why we are currently in immediate function context.
    Though, that flag alone can't be all the reasons for being in immediate
    function contexts, as I've tried the other reasons can't be handled in such
    a bool and need to be tested too.
    
    2021-10-27  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/102753
            * cp-tree.h (saved_scope): Document that consteval_if_p member
            is also set while processing immediate invocation.
            (in_immediate_context): Declare.
            * call.c (in_immediate_context): New function.
            (immediate_invocation_p): Use it.
            (struct in_consteval_if_p_temp_override): New class.
            (build_over_call): Temporarily set in_consteval_if_p for processing
            immediate invocation arguments.
            * typeck.c (cp_build_addr_expr_1): Diagnose taking address of
            an immediate method.  Use t instead of TREE_OPERAND (arg, 1).
            Use in_immediate_context function.
            * constexpr.c (find_immediate_fndecl): Handle PTRMEM_CST
            which refers to immediate function decl.
    
            * g++.dg/cpp2a/consteval13.C: Don't expect errors.
            * g++.dg/cpp2a/consteval20.C: New test.
            * g++.dg/cpp2a/consteval21.C: New test.
            * g++.dg/cpp2a/consteval22.C: New test.
            * g++.dg/cpp2a/consteval23.C: New test.
            * g++.dg/cpp23/consteval-if11.C: New test.

Diff:
---
 gcc/cp/call.c                               | 40 ++++++++++++++++++++++++-----
 gcc/cp/constexpr.c                          |  4 +++
 gcc/cp/cp-tree.h                            |  4 ++-
 gcc/cp/typeck.c                             | 16 +++++++++---
 gcc/testsuite/g++.dg/cpp23/consteval-if11.C | 27 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/consteval13.C    |  4 +--
 gcc/testsuite/g++.dg/cpp2a/consteval20.C    | 24 +++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/consteval21.C    | 35 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/consteval22.C    | 34 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/consteval23.C    | 13 ++++++++++
 10 files changed, 188 insertions(+), 13 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c5601d96ab8..20e66c6c63f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -9025,6 +9025,20 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref)
 		 instance, clobber);
 }
 
+/* Return true if in an immediate function context, or an unevaluated operand,
+   or a subexpression of an immediate invocation.  */
+
+bool
+in_immediate_context ()
+{
+  return (cp_unevaluated_operand != 0
+	  || (current_function_decl != NULL_TREE
+	      && DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
+	  || (current_binding_level->kind == sk_function_parms
+	      && current_binding_level->immediate_fn_ctx_p)
+	  || in_consteval_if_p);
+}
+
 /* Return true if a call to FN with number of arguments NARGS
    is an immediate invocation.  */
 
@@ -9033,18 +9047,25 @@ immediate_invocation_p (tree fn, int nargs)
 {
   return (TREE_CODE (fn) == FUNCTION_DECL
 	  && DECL_IMMEDIATE_FUNCTION_P (fn)
-	  && cp_unevaluated_operand == 0
-	  && (current_function_decl == NULL_TREE
-	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
-	  && (current_binding_level->kind != sk_function_parms
-	      || !current_binding_level->immediate_fn_ctx_p)
-	  && !in_consteval_if_p
+	  && !in_immediate_context ()
 	  /* As an exception, we defer std::source_location::current ()
 	     invocations until genericization because LWG3396 mandates
 	     special behavior for it.  */
 	  && (nargs > 1 || !source_location_current_p (fn)));
 }
 
+/* temp_override for in_consteval_if_p, which can't use make_temp_override
+   because it is a bitfield.  */
+
+struct in_consteval_if_p_temp_override {
+  bool save_in_consteval_if_p;
+  in_consteval_if_p_temp_override ()
+    : save_in_consteval_if_p (in_consteval_if_p) {}
+  void reset () { in_consteval_if_p = save_in_consteval_if_p; }
+  ~in_consteval_if_p_temp_override ()
+  { reset (); }
+};
+
 /* Subroutine of the various build_*_call functions.  Overload resolution
    has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
    ARGS is a TREE_LIST of the unconverted arguments to the call.  FLAGS is a
@@ -9254,6 +9275,12 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
     nargs = parmlen;
   argarray = XALLOCAVEC (tree, nargs);
 
+  in_consteval_if_p_temp_override icip;
+  /* If the call is immediate function invocation, make sure
+     taking address of immediate functions is allowed in its arguments.  */
+  if (immediate_invocation_p (STRIP_TEMPLATE (fn), nargs))
+    in_consteval_if_p = true;
+
   /* The implicit parameters to a constructor are not considered by overload
      resolution, and must be of the proper type.  */
   if (DECL_CONSTRUCTOR_P (fn))
@@ -9498,6 +9525,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 
   gcc_assert (j <= nargs);
   nargs = j;
+  icip.reset ();
 
   /* Avoid performing argument transformation if warnings are disabled.
      When tf_warning is set and at least one of the warnings is active
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6f83d303cdd..daa6358d08f 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -7276,6 +7276,10 @@ find_immediate_fndecl (tree *tp, int */*walk_subtrees*/, void */*data*/)
 {
   if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp))
     return *tp;
+  if (TREE_CODE (*tp) == PTRMEM_CST
+      && TREE_CODE (PTRMEM_CST_MEMBER (*tp)) == FUNCTION_DECL
+      && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (*tp)))
+    return PTRMEM_CST_MEMBER (*tp);
   return NULL_TREE;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5248ecd8fe7..f387b5036d2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1825,7 +1825,8 @@ struct GTY(()) saved_scope {
      if-statement.  */
   BOOL_BITFIELD discarded_stmt : 1;
   /* Nonzero if we are parsing or instantiating the compound-statement
-     of consteval if statement.  */
+     of consteval if statement.  Also set while processing an immediate
+     invocation.  */
   BOOL_BITFIELD consteval_if_p : 1;
 
   int unevaluated_operand;
@@ -6547,6 +6548,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
 extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, tsubst_flags_t);
 extern tree in_charge_arg_for_name		(tree);
+extern bool in_immediate_context		();
 extern tree build_cxx_call			(tree, int, tree *,
 						 tsubst_flags_t,
 						 tree = NULL_TREE);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index ab0f9da2552..d5f50001dc0 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -6773,9 +6773,19 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 	    return error_mark_node;
 	  }
 
+	if (TREE_CODE (t) == FUNCTION_DECL
+	    && DECL_IMMEDIATE_FUNCTION_P (t)
+	    && !in_immediate_context ())
+	  {
+	    if (complain & tf_error)
+	      error_at (loc, "taking address of an immediate function %qD",
+			t);
+	    return error_mark_node;
+	  }
+
 	type = build_ptrmem_type (context_for_name_lookup (t),
 				  TREE_TYPE (t));
-	t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1));
+	t = make_ptrmem_cst (type, t);
 	return t;
       }
 
@@ -6800,9 +6810,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
       tree stripped_arg = tree_strip_any_location_wrapper (arg);
       if (TREE_CODE (stripped_arg) == FUNCTION_DECL
 	  && DECL_IMMEDIATE_FUNCTION_P (stripped_arg)
-	  && cp_unevaluated_operand == 0
-	  && (current_function_decl == NULL_TREE
-	      || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)))
+	  && !in_immediate_context ())
 	{
 	  if (complain & tf_error)
 	    error_at (loc, "taking address of an immediate function %qD",
diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if11.C b/gcc/testsuite/g++.dg/cpp23/consteval-if11.C
new file mode 100644
index 00000000000..a22736cc006
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/consteval-if11.C
@@ -0,0 +1,27 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+struct S {
+  constexpr S () : s (0) {}
+  consteval int foo () { return 1; }
+  virtual consteval int bar () { return 2; }
+  int s;
+};
+
+consteval int foo () { return 42; }
+
+constexpr int
+bar ()
+{
+  if consteval {	// { dg-warning "'if consteval' only available with" "" { target c++20_only } }
+    int (*fn1) () = foo;
+    int (S::*fn2) () = &S::foo;
+    int (S::*fn3) () = &S::bar;
+    S s;
+    return fn1 () + (s.*fn2) () + (s.*fn3) ();
+  }
+  return 0;
+}
+
+static_assert (bar () == 45);
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval13.C b/gcc/testsuite/g++.dg/cpp2a/consteval13.C
index a2e1750b3ca..09ea6b70b6b 100644
--- a/gcc/testsuite/g++.dg/cpp2a/consteval13.C
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval13.C
@@ -10,8 +10,8 @@ void
 foo ()
 {
    auto qux = [] (fnptr a = quux ()) consteval { return a (); };
-   constexpr auto c = qux (baz);	// { dg-error "28:taking address of an immediate function" }
-   constexpr auto d = qux (bar);	// { dg-error "28:taking address of an immediate function" }
+   constexpr auto c = qux (baz);
+   constexpr auto d = qux (bar);
    static_assert (c == 1);
    static_assert (d == 42);
 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval20.C b/gcc/testsuite/g++.dg/cpp2a/consteval20.C
new file mode 100644
index 00000000000..2c359637c61
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval20.C
@@ -0,0 +1,24 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+
+struct S {
+  consteval int foo () const { return 42; }
+};
+
+constexpr S s;
+
+int
+bar ()
+{
+  return (s.*&S::foo) ();		// { dg-error "taking address of an immediate function" }
+}
+
+constexpr auto a = &S::foo;		// { dg-error "taking address of an immediate function" }
+
+consteval int
+baz ()
+{
+  return (s.*&S::foo) ();
+}
+
+static_assert (baz () == 42);
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval21.C b/gcc/testsuite/g++.dg/cpp2a/consteval21.C
new file mode 100644
index 00000000000..06ec705c75b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval21.C
@@ -0,0 +1,35 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+
+struct S {
+  constexpr S () : s (0) {}
+  consteval int foo () { return 1; }
+  virtual consteval int bar () { return 2; }
+  int s;
+};
+
+consteval int foo () { return 42; }
+
+consteval int
+bar (int (*fn) () = &foo)
+{
+  return fn ();
+}
+
+consteval int
+baz (int (S::*fn) () = &S::foo)
+{
+  S s;
+  return (s.*fn) ();
+}
+
+consteval int
+qux (int (S::*fn) () = &S::bar)
+{
+  S s;
+  return (s.*fn) ();
+}
+
+static_assert (bar () == 42);
+static_assert (baz () == 1);
+static_assert (qux () == 2);
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval22.C b/gcc/testsuite/g++.dg/cpp2a/consteval22.C
new file mode 100644
index 00000000000..5c3637189dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval22.C
@@ -0,0 +1,34 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+
+struct S {
+  constexpr S () : s (0) {}
+  consteval int foo () { return 1; }
+  virtual consteval int bar () { return 2; }
+  int s;
+};
+typedef int (S::*P) ();
+
+consteval P
+foo ()
+{
+  return &S::foo;
+}
+
+consteval P
+bar ()
+{
+  return &S::bar;
+}
+
+consteval int
+baz ()
+{
+  S s;
+  return (s.*(foo ())) () + (s.*(bar ())) ();
+}
+
+static_assert (baz () == 3);
+
+constexpr P a = foo ();		// { dg-error "immediate evaluation returns address of immediate function" }
+constexpr P b = bar ();		// { dg-error "immediate evaluation returns address of immediate function" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval23.C b/gcc/testsuite/g++.dg/cpp2a/consteval23.C
new file mode 100644
index 00000000000..4c7e844c450
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval23.C
@@ -0,0 +1,13 @@
+// PR c++/102753
+// { dg-do compile { target c++20 } }
+
+consteval int foo () { return 42; }
+
+consteval int
+bar (int (*fn) () = foo)
+{
+  return fn ();
+}
+
+static_assert (bar () == 42);
+static_assert (bar (foo) == 42);


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-10-27  7:28 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-27  7:28 [gcc r12-4730] c++: Diagnose taking address of an immediate member function [PR102753] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).