public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-2892] c++: Implement C++23 P1169R4 - static operator() [PR106651]
@ 2022-09-27  6:41 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2022-09-27  6:41 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:303976a6076f2839354702fd2caa049fa7cbbdc2

commit r13-2892-g303976a6076f2839354702fd2caa049fa7cbbdc2
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Tue Sep 27 08:36:28 2022 +0200

    c++: Implement C++23 P1169R4 - static operator() [PR106651]
    
    The following patch attempts to implement C++23 P1169R4 - static operator()
    paper's compiler side (there is some small library side too not implemented
    yet).  This allows static members as user operator() declarations and
    static specifier on lambdas without lambda capture.
    The synthetized conversion operator changes for static lambdas as it can just
    return the operator() static method address, doesn't need to create a thunk
    for it.
    The change in call.cc (joust) is to avoid ICEs because we assumed that len
    could be different only if both candidates are direct calls but it can be
    one direct and one indirect call, and to implement the
    [over.match.best.general]/1 and [over.best.ics.general] changes from
    the paper (implemented always as Jason is sure it doesn't make a difference
    in C++20 and earlier unless static member function operator() or
    static lambda which we accept with pedwarn in earlier standards too appears
    and my testing confirmed that).
    
    2022-09-27  Jakub Jelinek  <jakub@redhat.com>
    
            PR c++/106651
    gcc/c-family/
            * c-cppbuiltin.cc (c_cpp_builtins): Predefine
            __cpp_static_call_operator=202207L for C++23.
    gcc/cp/
            * cp-tree.h (LAMBDA_EXPR_STATIC_P): Implement C++23
            P1169R4 - static operator().  Define.
            * parser.cc (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): Document
            that it also allows static.
            (cp_parser_lambda_declarator_opt): Handle static lambda specifier.
            (cp_parser_decl_specifier_seq): Allow RID_STATIC for
            CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR.
            * decl.cc (grok_op_properties): If operator() isn't a method,
            use a different error wording, if it is static member function,
            allow it (for C++20 and older with a pedwarn unless it is
            a lambda function or template instantiation).
            * call.cc (joust): Don't ICE if one candidate is static member
            function and the other is an indirect call.  If the parameter
            conversion on the other candidate is user defined conversion,
            ellipsis or bad conversion, make static member function candidate
            a winner for that parameter.
            * lambda.cc (maybe_add_lambda_conv_op): Handle static lambdas.
            * error.cc (dump_lambda_function): Print static for static lambdas.
    gcc/testsuite/
            * g++.dg/template/error30.C: Adjust expected diagnostics.
            * g++.dg/cpp1z/constexpr-lambda13.C: Likewise.
            * g++.dg/cpp23/feat-cxx2b.C: Test __cpp_static_call_operator.
            * g++.dg/cpp23/static-operator-call1.C: New test.
            * g++.dg/cpp23/static-operator-call2.C: New test.
            * g++.old-deja/g++.jason/operator.C: Adjust expected diagnostics.

Diff:
---
 gcc/c-family/c-cppbuiltin.cc                       |  1 +
 gcc/cp/call.cc                                     | 25 ++++--
 gcc/cp/cp-tree.h                                   |  5 ++
 gcc/cp/decl.cc                                     | 19 ++++-
 gcc/cp/error.cc                                    |  8 +-
 gcc/cp/lambda.cc                                   | 89 +++++++++++++++++-----
 gcc/cp/parser.cc                                   | 36 +++++++--
 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C    |  2 +-
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C            |  6 ++
 gcc/testsuite/g++.dg/cpp23/static-operator-call1.C | 41 ++++++++++
 gcc/testsuite/g++.dg/cpp23/static-operator-call2.C | 22 ++++++
 gcc/testsuite/g++.dg/cpp23/static-operator-call3.C | 10 +++
 gcc/testsuite/g++.dg/template/error30.C            |  2 +-
 gcc/testsuite/g++.old-deja/g++.jason/operator.C    |  2 +-
 14 files changed, 235 insertions(+), 33 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index e3f4d3d4565..ca5f500e079 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1081,6 +1081,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_constexpr=202110L");
 	  cpp_define (pfile, "__cpp_multidimensional_subscript=202110L");
 	  cpp_define (pfile, "__cpp_named_character_escapes=202207L");
+	  cpp_define (pfile, "__cpp_static_call_operator=202207L");
 	}
       if (flag_concepts)
         {
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 3b7e5271de4..fc86b74a5a4 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -12261,10 +12261,14 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
   len = cand1->num_convs;
   if (len != cand2->num_convs)
     {
-      int static_1 = DECL_STATIC_FUNCTION_P (cand1->fn);
-      int static_2 = DECL_STATIC_FUNCTION_P (cand2->fn);
-
-      if (DECL_CONSTRUCTOR_P (cand1->fn)
+      int static_1 = (TREE_CODE (cand1->fn) == FUNCTION_DECL
+		      && DECL_STATIC_FUNCTION_P (cand1->fn));
+      int static_2 = (TREE_CODE (cand2->fn) == FUNCTION_DECL
+		      && DECL_STATIC_FUNCTION_P (cand2->fn));
+
+      if (TREE_CODE (cand1->fn) == FUNCTION_DECL
+	  && TREE_CODE (cand2->fn) == FUNCTION_DECL
+	  && DECL_CONSTRUCTOR_P (cand1->fn)
 	  && is_list_ctor (cand1->fn) != is_list_ctor (cand2->fn))
 	/* We're comparing a near-match list constructor and a near-match
 	   non-list constructor.  Just treat them as unordered.  */
@@ -12273,9 +12277,20 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
       gcc_assert (static_1 != static_2);
 
       if (static_1)
-	off2 = 1;
+	{
+	  /* C++23 [over.best.ics.general] says:
+	     When the parameter is the implicit object parameter of a static
+	     member function, the implicit conversion sequence is a standard
+	     conversion sequence that is neither better nor worse than any
+	     other standard conversion sequence.  */
+	  if (CONVERSION_RANK (cand2->convs[0]) >= cr_user)
+	    winner = 1;
+	  off2 = 1;
+	}
       else
 	{
+	  if (CONVERSION_RANK (cand1->convs[0]) >= cr_user)
+	    winner = -1;
 	  off1 = 1;
 	  --len;
 	}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 51fd0cf8ba9..99b486b8002 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       OVL_NESTED_P (in OVERLOAD)
       DECL_MODULE_EXPORT_P (in _DECL)
       PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
+      LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
    4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
       TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
 	  CALL_EXPR, or FIELD_DECL).
@@ -1490,6 +1491,10 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
   TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
 
+/* Predicate tracking whether the lambda was declared 'static'.  */
+#define LAMBDA_EXPR_STATIC_P(NODE) \
+  TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE))
+
 /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit
    capture.  */
 #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index f4460c911b3..fb85564a191 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -15300,8 +15300,25 @@ grok_op_properties (tree decl, bool complain)
      an enumeration, or a reference to an enumeration.  13.4.0.6 */
   if (! methodp || DECL_STATIC_FUNCTION_P (decl))
     {
+      if (operator_code == CALL_EXPR)
+	{
+	  if (! DECL_STATIC_FUNCTION_P (decl))
+	    {
+	      error_at (loc, "%qD must be a member function", decl);
+	      return false;
+	    }
+	  if (cxx_dialect < cxx23
+	      /* For lambdas we diagnose static lambda specifier elsewhere.  */
+	      && ! LAMBDA_FUNCTION_P (decl)
+	      /* For instantiations, we have diagnosed this already.  */
+	      && ! DECL_USE_TEMPLATE (decl))
+	    pedwarn (loc, OPT_Wc__23_extensions, "%qD may be a static member "
+	      "function only with %<-std=c++23%> or %<-std=gnu++23%>", decl);
+	  /* There are no further restrictions on the arguments to an
+	     overloaded "operator ()".  */
+	  return true;
+	}
       if (operator_code == TYPE_EXPR
-	  || operator_code == CALL_EXPR
 	  || operator_code == COMPONENT_REF
 	  || operator_code == ARRAY_REF
 	  || operator_code == NOP_EXPR)
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 94181e76d0e..0389f35d731 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer *pp,
 {
   /* A lambda's signature is essentially its "type".  */
   dump_type (pp, DECL_CONTEXT (fn), flags);
-  if (!(TYPE_QUALS (class_of_this_parm (TREE_TYPE (fn))) & TYPE_QUAL_CONST))
+  if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
+    {
+      pp->padding = pp_before;
+      pp_c_ws_string (pp, "static");
+    }
+  else if (!(TYPE_QUALS (class_of_this_parm (TREE_TYPE (fn)))
+	     & TYPE_QUAL_CONST))
     {
       pp->padding = pp_before;
       pp_c_ws_string (pp, "mutable");
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index 3ee1fe9489e..e9d5d4dc1c5 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -1099,7 +1099,9 @@ maybe_add_lambda_conv_op (tree type)
   tree optype = TREE_TYPE (callop);
   tree fn_result = TREE_TYPE (optype);
 
-  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
+  tree thisarg = NULL_TREE;
+  if (TREE_CODE (optype) == METHOD_TYPE)
+    thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
   if (generic_lambda_p)
     {
       ++processing_template_decl;
@@ -1109,18 +1111,25 @@ maybe_add_lambda_conv_op (tree type)
 	 return expression for a deduced return call op to allow for simple
 	 implementation of the conversion operator.  */
 
-      tree instance = cp_build_fold_indirect_ref (thisarg);
-      tree objfn = lookup_template_function (DECL_NAME (callop),
-					     DECL_TI_ARGS (callop));
-      objfn = build_min (COMPONENT_REF, NULL_TREE,
-			 instance, objfn, NULL_TREE);
-      int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
+      tree objfn;
+      int nargs = list_length (DECL_ARGUMENTS (callop));
+      if (thisarg)
+	{
+	  tree instance = cp_build_fold_indirect_ref (thisarg);
+	  objfn = lookup_template_function (DECL_NAME (callop),
+					    DECL_TI_ARGS (callop));
+	  objfn = build_min (COMPONENT_REF, NULL_TREE,
+			     instance, objfn, NULL_TREE);
+	  --nargs;
+	  call = prepare_op_call (objfn, nargs);
+	}
+      else
+	objfn = callop;
 
-      call = prepare_op_call (objfn, nargs);
       if (type_uses_auto (fn_result))
 	decltype_call = prepare_op_call (objfn, nargs);
     }
-  else
+  else if (thisarg)
     {
       direct_argvec = make_tree_vector ();
       direct_argvec->quick_push (thisarg);
@@ -1135,9 +1144,11 @@ maybe_add_lambda_conv_op (tree type)
   tree fn_args = NULL_TREE;
   {
     int ix = 0;
-    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
+    tree src = FUNCTION_FIRST_USER_PARM (callop);
     tree tgt = NULL;
 
+    if (!thisarg && !decltype_call)
+      src = NULL_TREE;
     while (src)
       {
 	tree new_node = copy_node (src);
@@ -1160,12 +1171,15 @@ maybe_add_lambda_conv_op (tree type)
 	if (generic_lambda_p)
 	  {
 	    tree a = tgt;
-	    if (DECL_PACK_P (tgt))
+	    if (thisarg)
 	      {
-		a = make_pack_expansion (a);
-		PACK_EXPANSION_LOCAL_P (a) = true;
+		if (DECL_PACK_P (tgt))
+		  {
+		    a = make_pack_expansion (a);
+		    PACK_EXPANSION_LOCAL_P (a) = true;
+		  }
+		CALL_EXPR_ARG (call, ix) = a;
 	      }
-	    CALL_EXPR_ARG (call, ix) = a;
 
 	    if (decltype_call)
 	      {
@@ -1193,7 +1207,7 @@ maybe_add_lambda_conv_op (tree type)
 	     tf_warning_or_error);
 	}
     }
-  else
+  else if (thisarg)
     {
       /* Don't warn on deprecated or unavailable lambda declarations, unless
 	 the lambda is actually called.  */
@@ -1203,10 +1217,14 @@ maybe_add_lambda_conv_op (tree type)
 			   direct_argvec->address ());
     }
 
-  CALL_FROM_THUNK_P (call) = 1;
-  SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
+  if (thisarg)
+    {
+      CALL_FROM_THUNK_P (call) = 1;
+      SET_EXPR_LOCATION (call, UNKNOWN_LOCATION);
+    }
 
-  tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
+  tree stattype
+    = build_function_type (fn_result, FUNCTION_FIRST_USER_PARMTYPE (callop));
   stattype = (cp_build_type_attribute_variant
 	      (stattype, TYPE_ATTRIBUTES (optype)));
   if (flag_noexcept_type
@@ -1249,6 +1267,41 @@ maybe_add_lambda_conv_op (tree type)
 
   add_method (type, fn, false);
 
+  if (thisarg == NULL_TREE)
+    {
+      /* For static lambda, just return operator().  */
+      if (nested)
+	push_function_context ();
+      else
+	/* Still increment function_depth so that we don't GC in the
+	   middle of an expression.  */
+	++function_depth;
+
+      /* Generate the body of the conversion op.  */
+
+      start_preparsed_function (convfn, NULL_TREE,
+				SF_PRE_PARSED | SF_INCLASS_INLINE);
+      tree body = begin_function_body ();
+      tree compound_stmt = begin_compound_stmt (0);
+
+      /* decl_needed_p needs to see that it's used.  */
+      TREE_USED (callop) = 1;
+      finish_return_stmt (decay_conversion (callop, tf_warning_or_error));
+
+      finish_compound_stmt (compound_stmt);
+      finish_function_body (body);
+
+      fn = finish_function (/*inline_p=*/true);
+      if (!generic_lambda_p)
+	expand_or_defer_fn (fn);
+
+      if (nested)
+	pop_function_context ();
+      else
+	--function_depth;
+      return;
+    }
+
   /* Generic thunk code fails for varargs; we'll complain in mark_used if
      the conversion op is used.  */
   if (varargs_function_p (callop))
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 4bd3ca1ac19..d501178634a 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -1996,7 +1996,7 @@ enum
      constexpr.  */
   CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8,
   /* When parsing a decl-specifier-seq, only allow mutable, constexpr or
-     for C++20 consteval.  */
+     for C++20 consteval or for C++23 static.  */
   CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
   /* When parsing a decl-specifier-seq, allow missing typename.  */
   CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20,
@@ -11731,6 +11731,18 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
       quals = TYPE_UNQUALIFIED;
     }
+  else if (lambda_specs.storage_class == sc_static)
+    {
+      if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+	  || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
+	error_at (lambda_specs.locations[ds_storage_class],
+		  "%<static%> lambda specifier with lambda capture");
+      else
+	{
+	  LAMBDA_EXPR_STATIC_P (lambda_expr) = 1;
+	  quals = TYPE_UNQUALIFIED;
+	}
+    }
 
   tx_qual = cp_parser_tx_qualifier_opt (parser);
   if (omitted_parms_loc && tx_qual)
@@ -11816,6 +11828,12 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
     if (lambda_specs.locations[ds_consteval])
       return_type_specs.locations[ds_consteval]
 	= lambda_specs.locations[ds_consteval];
+    if (LAMBDA_EXPR_STATIC_P (lambda_expr))
+      {
+	return_type_specs.storage_class = sc_static;
+	return_type_specs.locations[ds_storage_class]
+	  = lambda_specs.locations[ds_storage_class];
+      }
 
     p = obstack_alloc (&declarator_obstack, 0);
 
@@ -11839,8 +11857,9 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
       {
 	DECL_INITIALIZED_IN_CLASS_P (fco) = 1;
 	DECL_ARTIFICIAL (fco) = 1;
-	/* Give the object parameter a different name.  */
-	DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
+	if (!LAMBDA_EXPR_STATIC_P (lambda_expr))
+	  /* Give the object parameter a different name.  */
+	  DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
 	DECL_SET_LAMBDA_FUNCTION (fco, true);
       }
     if (template_param_list)
@@ -16034,8 +16053,15 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
 	  && token->keyword != RID_MUTABLE
 	  && token->keyword != RID_CONSTEXPR
 	  && token->keyword != RID_CONSTEVAL)
-	error_at (token->location, "%qD invalid in lambda",
-		  ridpointers[token->keyword]);
+	{
+	  if (token->keyword != RID_STATIC)
+	    error_at (token->location, "%qD invalid in lambda",
+		      ridpointers[token->keyword]);
+	  else if (cxx_dialect < cxx23)
+	    pedwarn (token->location, OPT_Wc__23_extensions,
+		     "%qD only valid in lambda with %<-std=c++23%> or "
+		     "%<-std=gnu++23%>", ridpointers[token->keyword]);
+	}
 
       if (ds != ds_last)
 	set_and_check_decl_spec_loc (decl_specs, ds, token);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
index 962ec8db62c..0323a0d05fc 100644
--- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C
@@ -2,4 +2,4 @@
 
 auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
 auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
-auto l3 = []() static { };	    // { dg-error "static" }
+auto l3 = []() static { };	    // { dg-error "static' only valid in lambda with" "" { target c++20_down } }
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 0537e1d24b5..2f6b21ead70 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -563,3 +563,9 @@
 #elif __cpp_named_character_escapes != 202207
 #  error "__cpp_named_character_escapes != 202207"
 #endif
+
+#ifndef __cpp_static_call_operator
+#  error "__cpp_static_call_operator"
+#elif __cpp_static_call_operator != 202207
+#  error "__cpp_static_call_operator != 202207"
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp23/static-operator-call1.C b/gcc/testsuite/g++.dg/cpp23/static-operator-call1.C
new file mode 100644
index 00000000000..42219bfd862
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/static-operator-call1.C
@@ -0,0 +1,41 @@
+// P1169R4 - static operator()
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+template <typename T>
+struct S
+{
+  static constexpr bool operator () (T const &x, T const &y) { return x < y; }; // { dg-warning "may be a static member function only with" "" { target c++20_down } }
+  using P = bool (*) (T const &, T const &);
+  operator P () const { return operator (); }
+};
+
+static_assert (S<int> {} (1, 2), "");
+
+template <typename T>
+void
+bar (T &x)
+{
+  x (1, 2);
+}
+
+void
+foo ()
+{
+#if __cpp_constexpr >= 201603L
+  auto a = [](int x, int y) static constexpr { return x + y; };			// { dg-warning "'static' only valid in lambda with" "" { target { c++17 && c++20_down } } }
+  static_assert (a (1, 2) == 3, "");
+  bar (*a);
+#endif
+  auto b = []() static { return 1; };						// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } }
+  b ();
+  auto c = [](int x, int y) static { return x + y; };				// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } }
+  c (1, 2);
+  bar (*c);
+#if __cpp_generic_lambdas >= 201707L
+  auto d = []<typename T, typename U>(T x, U y) static { return x + y; };	// { dg-warning "'static' only valid in lambda with" "" { target c++20_only } }
+  d (1, 2L);
+#endif
+  S<long> s;
+  s(1L, 2L);
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/static-operator-call2.C b/gcc/testsuite/g++.dg/cpp23/static-operator-call2.C
new file mode 100644
index 00000000000..21f3d448497
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/static-operator-call2.C
@@ -0,0 +1,22 @@
+// P1169R4 - static operator()
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void
+foo ()
+{
+  int u = 0;
+  auto a = [](int x, int y) mutable mutable { return x + y; };		// { dg-error "duplicate 'mutable' specifier" }
+  auto b = [](int x, int y) static static { return x + y; };		// { dg-error "duplicate 'static' specifier" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+  auto c = [](int x, int y) static mutable { return x + y; };		// { dg-error "'mutable' specifier conflicts with 'static'" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+  auto d = [](int x, int y) mutable static { return x + y; };		// { dg-error "'static' specifier conflicts with 'mutable'" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+  auto e = [=](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+  auto f = [&](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+  auto g = [u](int x, int y) static { return x + y; };			// { dg-error "lambda specifier with lambda capture" }
+									// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/static-operator-call3.C b/gcc/testsuite/g++.dg/cpp23/static-operator-call3.C
new file mode 100644
index 00000000000..9c84db62156
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/static-operator-call3.C
@@ -0,0 +1,10 @@
+// P1169R4 - static operator()
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+void
+foo ()
+{
+  auto a = [] (auto x) static { return x; };					// { dg-warning "'static' only valid in lambda with" "" { target c++20_down } }
+  int (*b) (int) = a;
+}
diff --git a/gcc/testsuite/g++.dg/template/error30.C b/gcc/testsuite/g++.dg/template/error30.C
index 3a87872d526..5a3047c11c4 100644
--- a/gcc/testsuite/g++.dg/template/error30.C
+++ b/gcc/testsuite/g++.dg/template/error30.C
@@ -2,4 +2,4 @@
 
 template<int> struct A;
 
-template<template<typename> class B> A<B<int>::x> operator() (); // { dg-error "51:.A<B<int>::x> operator\\(\\)\\(\\). must be a non-static member function" }
+template<template<typename> class B> A<B<int>::x> operator() (); // { dg-error "51:.A<B<int>::x> operator\\(\\)\\(\\). must be a member function" }
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/operator.C b/gcc/testsuite/g++.old-deja/g++.jason/operator.C
index 79c1932a353..c18790190b5 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/operator.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/operator.C
@@ -6,7 +6,7 @@ typedef __SIZE_TYPE__ size_t;
 
 struct A {
   int operator?:(int a, int b);	   // { dg-error "prohibits overloading" } 
-  static int operator()(int a);	   // { dg-error "14:.static int A::operator\\(\\)\\(int\\). must be a non-static member function" }
+  static int operator()(int a);	   // { dg-warning "14:.static int A::operator\\(\\)\\(int\\). may be a static member function only with" "" { target c++20_down } }
   static int operator+(A,A);	   // { dg-error "14:.static int A::operator\\+\\(A, A\\). must be either a non-static member function or a non-member function" } 
   int operator+(int a, int b = 1); // { dg-error "7:.int A::operator\\+\\(int, int\\). must have either zero or one argument" }
   int operator++(char);		   // { dg-error "7:postfix .int A::operator\\+\\+\\(char\\). must have .int. as its argument" }

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

only message in thread, other threads:[~2022-09-27  6:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-27  6:41 [gcc r13-2892] c++: Implement C++23 P1169R4 - static operator() [PR106651] 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).