public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651]
@ 2022-09-13 16:42 Jakub Jelinek
  2022-09-16 23:23 ` Jason Merrill
  0 siblings, 1 reply; 8+ messages in thread
From: Jakub Jelinek @ 2022-09-13 16:42 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

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.  As decl specifier
parsing doesn't track about the presence and locations of all specifiers,
the patch unfortunately replaces the diagnostics about duplicate mutable
with diagnostics about conflicting specifiers because the information
whether it was mutable mutable, mutable static, static mutable or static
static is lost.  Beyond this, 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 I'm least sure about is the call.cc (joust) change, one thing
is that we ICEd 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 then I'm trying to implement my understanding of the
[over.match.best.general]/1 and [over.best.ics.general] changes from
the paper (implemented both for C++23 and when the static member function
is operator() which we accept with pedwarn in earlier standards too).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-09-13  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.  For C++23 or if
	the static member is operator() and 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.

--- gcc/cp/cp-tree.h.jj	2022-09-13 09:21:28.052541628 +0200
+++ gcc/cp/cp-tree.h	2022-09-13 12:14:31.674733861 +0200
@@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       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).
@@ -1488,6 +1489,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) \
--- gcc/cp/parser.cc.jj	2022-09-13 09:21:01.276920558 +0200
+++ gcc/cp/parser.cc	2022-09-13 12:14:31.683733738 +0200
@@ -1994,7 +1994,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,
@@ -11714,13 +11714,26 @@ cp_parser_lambda_declarator_opt (cp_pars
       omitted_parms_loc = UNKNOWN_LOCATION;
     }
 
-  if (lambda_specs.storage_class == sc_mutable)
+  if (lambda_specs.storage_class == sc_mutable
+      || lambda_specs.storage_class == sc_static)
     {
-      LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
-      quals = TYPE_UNQUALIFIED;
+      if (lambda_specs.storage_class == sc_mutable)
+	{
+	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+	  quals = TYPE_UNQUALIFIED;
+	}
+      else 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;
+	}
       if (lambda_specs.conflicting_specifiers_p)
 	error_at (lambda_specs.locations[ds_storage_class],
-		  "duplicate %<mutable%>");
+		  "conflicting lambda specifiers");
     }
 
   tx_qual = cp_parser_tx_qualifier_opt (parser);
@@ -11807,6 +11820,12 @@ cp_parser_lambda_declarator_opt (cp_pars
     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);
 
@@ -11830,8 +11849,9 @@ cp_parser_lambda_declarator_opt (cp_pars
       {
 	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)
@@ -16008,8 +16028,15 @@ cp_parser_decl_specifier_seq (cp_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);
--- gcc/cp/decl.cc.jj	2022-09-13 09:21:28.062541487 +0200
+++ gcc/cp/decl.cc	2022-09-13 12:14:31.677733820 +0200
@@ -15299,8 +15299,25 @@ grok_op_properties (tree decl, bool comp
      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)
--- gcc/cp/call.cc.jj	2022-09-13 09:21:28.008542251 +0200
+++ gcc/cp/call.cc	2022-09-13 12:34:10.307510932 +0200
@@ -12133,10 +12133,14 @@ joust (struct z_candidate *cand1, struct
   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);
+      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 (DECL_CONSTRUCTOR_P (cand1->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.  */
@@ -12145,9 +12149,29 @@ joust (struct z_candidate *cand1, struct
       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.
+	     Apply this for C++23 or when the static member function is
+	     overloaded call operator (C++23 feature we accept as an
+	     extension).  */
+	  if ((cxx_dialect >= cxx23
+	       || (DECL_OVERLOADED_OPERATOR_P (cand1->fn)
+		   && DECL_OVERLOADED_OPERATOR_IS (cand1->fn, CALL_EXPR)))
+	      && CONVERSION_RANK (cand2->convs[0]) >= cr_user)
+	    winner = -1;
+	  off2 = 1;
+	}
       else
 	{
+	  if ((cxx_dialect >= cxx23
+	       || (DECL_OVERLOADED_OPERATOR_P (cand2->fn)
+		   && DECL_OVERLOADED_OPERATOR_IS (cand2->fn, CALL_EXPR)))
+	      && CONVERSION_RANK (cand1->convs[0]) >= cr_user)
+	    winner = -1;
 	  off1 = 1;
 	  --len;
 	}
--- gcc/cp/lambda.cc.jj	2022-09-13 09:21:28.119540680 +0200
+++ gcc/cp/lambda.cc	2022-09-13 12:14:31.674733861 +0200
@@ -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,22 @@ 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;
+      int nargs = list_length (DECL_ARGUMENTS (callop));
+      if (thisarg)
+	{
+	  tree instance = cp_build_fold_indirect_ref (thisarg);
+	  objfn = build_min (COMPONENT_REF, NULL_TREE,
+			     instance, objfn, NULL_TREE);
+	  --nargs;
+	  call = prepare_op_call (objfn, nargs);
+	}
 
-      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 +1141,13 @@ maybe_add_lambda_conv_op (tree type)
   tree fn_args = NULL_TREE;
   {
     int ix = 0;
-    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
+    tree src = DECL_ARGUMENTS (callop);
     tree tgt = NULL;
 
+    if (thisarg)
+      src = DECL_CHAIN (src);
+    else if (!decltype_call)
+      src = NULL_TREE;
     while (src)
       {
 	tree new_node = copy_node (src);
@@ -1160,12 +1170,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 +1206,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,14 +1216,18 @@ 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));
-  stattype = (cp_build_type_attribute_variant
-	      (stattype, TYPE_ATTRIBUTES (optype)));
-  if (flag_noexcept_type
-      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
+  tree stattype
+    = build_function_type (fn_result, thisarg ? FUNCTION_ARG_CHAIN (callop)
+				      : TYPE_ARG_TYPES (optype));
+  stattype = cp_build_type_attribute_variant (stattype,
+					      TYPE_ATTRIBUTES (optype));
+  if (flag_noexcept_type && TYPE_NOTHROW_P (TREE_TYPE (callop)))
     stattype = build_exception_variant (stattype, noexcept_true_spec);
 
   if (generic_lambda_p)
@@ -1249,6 +1266,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))
--- gcc/cp/error.cc.jj	2022-09-13 09:21:01.219921365 +0200
+++ gcc/cp/error.cc	2022-09-13 12:14:31.675733848 +0200
@@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer
 {
   /* 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");
--- gcc/c-family/c-cppbuiltin.cc.jj	2022-09-13 09:21:00.986924662 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2022-09-13 12:14:31.683733738 +0200
@@ -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)
         {
--- gcc/testsuite/g++.dg/template/error30.C.jj	2020-05-07 23:10:25.242966216 +0200
+++ gcc/testsuite/g++.dg/template/error30.C	2022-09-13 14:43:49.624224804 +0200
@@ -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" }
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C.jj	2020-01-14 20:02:46.786609789 +0100
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-13 14:45:42.659698626 +0200
@@ -1,5 +1,5 @@
 // { dg-do compile { target c++17 } }
 
 auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
-auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
-auto l3 = []() static { };	    // { dg-error "static" }
+auto l2 = []() mutable mutable { }; // { dg-error "conflicting lambda specifiers" }
+auto l3 = []() static { };	    // { dg-error "static' only valid in lambda with" "" { target c++20_down } }
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-08-26 09:24:12.171614876 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-13 15:05:16.557821712 +0200
@@ -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
--- gcc/testsuite/g++.dg/cpp23/static-operator-call1.C.jj	2022-09-13 13:01:34.251875648 +0200
+++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-13 13:00:51.344457645 +0200
@@ -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);
+}
--- gcc/testsuite/g++.dg/cpp23/static-operator-call2.C.jj	2022-09-13 13:01:56.307576484 +0200
+++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-13 13:30:24.092305299 +0200
@@ -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 "conflicting lambda specifiers" }
+  auto b = [](int x, int y) static static { return x + y; };		// { dg-error "conflicting lambda specifiers" }
+									// { 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 "conflicting lambda specifiers" }
+									// { 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 "conflicting lambda specifiers" }
+									// { 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 }
+}
--- gcc/testsuite/g++.old-deja/g++.jason/operator.C.jj	2020-05-07 23:10:25.242966216 +0200
+++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-13 14:48:43.219257874 +0200
@@ -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" }

	Jakub


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651]
  2022-09-13 16:42 [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651] Jakub Jelinek
@ 2022-09-16 23:23 ` Jason Merrill
  2022-09-17  6:42   ` Jakub Jelinek
  2022-09-19  7:24   ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
  0 siblings, 2 replies; 8+ messages in thread
From: Jason Merrill @ 2022-09-16 23:23 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 9/13/22 12:42, Jakub Jelinek wrote:
> Hi!
> 
> 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.  As decl specifier
> parsing doesn't track about the presence and locations of all specifiers,
> the patch unfortunately replaces the diagnostics about duplicate mutable
> with diagnostics about conflicting specifiers because the information
> whether it was mutable mutable, mutable static, static mutable or static
> static is lost.

I wonder why we don't give an error when setting the 
conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should 
be able to give a better diagnostic at that point.

> Beyond this, 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 I'm least sure about is the call.cc (joust) change, one thing
> is that we ICEd 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,

How do you mean?

> and then I'm trying to implement my understanding of the
> [over.match.best.general]/1 and [over.best.ics.general] changes from
> the paper (implemented both for C++23 and when the static member function
> is operator() which we accept with pedwarn in earlier standards too).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2022-09-13  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.  For C++23 or if
> 	the static member is operator() and 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.
> 
> --- gcc/cp/cp-tree.h.jj	2022-09-13 09:21:28.052541628 +0200
> +++ gcc/cp/cp-tree.h	2022-09-13 12:14:31.674733861 +0200
> @@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI
>         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).
> @@ -1488,6 +1489,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) \
> --- gcc/cp/parser.cc.jj	2022-09-13 09:21:01.276920558 +0200
> +++ gcc/cp/parser.cc	2022-09-13 12:14:31.683733738 +0200
> @@ -1994,7 +1994,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,
> @@ -11714,13 +11714,26 @@ cp_parser_lambda_declarator_opt (cp_pars
>         omitted_parms_loc = UNKNOWN_LOCATION;
>       }
>   
> -  if (lambda_specs.storage_class == sc_mutable)
> +  if (lambda_specs.storage_class == sc_mutable
> +      || lambda_specs.storage_class == sc_static)
>       {
> -      LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
> -      quals = TYPE_UNQUALIFIED;
> +      if (lambda_specs.storage_class == sc_mutable)
> +	{
> +	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
> +	  quals = TYPE_UNQUALIFIED;
> +	}
> +      else 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;
> +	}
>         if (lambda_specs.conflicting_specifiers_p)
>   	error_at (lambda_specs.locations[ds_storage_class],
> -		  "duplicate %<mutable%>");
> +		  "conflicting lambda specifiers");
>       }
>   
>     tx_qual = cp_parser_tx_qualifier_opt (parser);
> @@ -11807,6 +11820,12 @@ cp_parser_lambda_declarator_opt (cp_pars
>       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);
>   
> @@ -11830,8 +11849,9 @@ cp_parser_lambda_declarator_opt (cp_pars
>         {
>   	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)
> @@ -16008,8 +16028,15 @@ cp_parser_decl_specifier_seq (cp_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);
> --- gcc/cp/decl.cc.jj	2022-09-13 09:21:28.062541487 +0200
> +++ gcc/cp/decl.cc	2022-09-13 12:14:31.677733820 +0200
> @@ -15299,8 +15299,25 @@ grok_op_properties (tree decl, bool comp
>        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)
> --- gcc/cp/call.cc.jj	2022-09-13 09:21:28.008542251 +0200
> +++ gcc/cp/call.cc	2022-09-13 12:34:10.307510932 +0200
> @@ -12133,10 +12133,14 @@ joust (struct z_candidate *cand1, struct
>     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);
> +      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 (DECL_CONSTRUCTOR_P (cand1->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.  */
> @@ -12145,9 +12149,29 @@ joust (struct z_candidate *cand1, struct
>         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.
> +	     Apply this for C++23 or when the static member function is
> +	     overloaded call operator (C++23 feature we accept as an
> +	     extension).  */
> +	  if ((cxx_dialect >= cxx23
> +	       || (DECL_OVERLOADED_OPERATOR_P (cand1->fn)
> +		   && DECL_OVERLOADED_OPERATOR_IS (cand1->fn, CALL_EXPR)))
> +	      && CONVERSION_RANK (cand2->convs[0]) >= cr_user)
> +	    winner = -1;

I don't know what you're trying to do here.  The above passage describes 
how we've always compared static to non-static, which should work the 
same way for operator().

> +	  off2 = 1;
> +	}
>         else
>   	{
> +	  if ((cxx_dialect >= cxx23
> +	       || (DECL_OVERLOADED_OPERATOR_P (cand2->fn)
> +		   && DECL_OVERLOADED_OPERATOR_IS (cand2->fn, CALL_EXPR)))
> +	      && CONVERSION_RANK (cand1->convs[0]) >= cr_user)
> +	    winner = -1;
>   	  off1 = 1;
>   	  --len;
>   	}
> --- gcc/cp/lambda.cc.jj	2022-09-13 09:21:28.119540680 +0200
> +++ gcc/cp/lambda.cc	2022-09-13 12:14:31.674733861 +0200
> @@ -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,22 @@ 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;
> +      int nargs = list_length (DECL_ARGUMENTS (callop));
> +      if (thisarg)
> +	{
> +	  tree instance = cp_build_fold_indirect_ref (thisarg);
> +	  objfn = build_min (COMPONENT_REF, NULL_TREE,
> +			     instance, objfn, NULL_TREE);
> +	  --nargs;
> +	  call = prepare_op_call (objfn, nargs);
> +	}
>   
> -      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 +1141,13 @@ maybe_add_lambda_conv_op (tree type)
>     tree fn_args = NULL_TREE;
>     {
>       int ix = 0;
> -    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
> +    tree src = DECL_ARGUMENTS (callop);
>       tree tgt = NULL;
>   
> +    if (thisarg)
> +      src = DECL_CHAIN (src);

You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.

> +    else if (!decltype_call)
> +      src = NULL_TREE;
>       while (src)
>         {
>   	tree new_node = copy_node (src);
> @@ -1160,12 +1170,15 @@ maybe_add_lambda_conv_op (tree type)
>   	if (generic_lambda_p)
>   	  {
>   	    tree a = tgt;
> -	    if (DECL_PACK_P (tgt))
> +	    if (thisarg)

This is wrong, pack expansion handling has nothing to do with 'this'. 
Only the assignment to CALL_EXPR_ARG should depend on 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 +1206,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,14 +1216,18 @@ 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));
> -  stattype = (cp_build_type_attribute_variant
> -	      (stattype, TYPE_ATTRIBUTES (optype)));
> -  if (flag_noexcept_type
> -      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
> +  tree stattype
> +    = build_function_type (fn_result, thisarg ? FUNCTION_ARG_CHAIN (callop)
> +				      : TYPE_ARG_TYPES (optype));
> +  stattype = cp_build_type_attribute_variant (stattype,
> +					      TYPE_ATTRIBUTES (optype));
> +  if (flag_noexcept_type && TYPE_NOTHROW_P (TREE_TYPE (callop)))
>       stattype = build_exception_variant (stattype, noexcept_true_spec);

If !thisarg, shouldn't stattype just be optype?  No need to rebuild it 
in that case.

>     if (generic_lambda_p)
> @@ -1249,6 +1266,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))
> --- gcc/cp/error.cc.jj	2022-09-13 09:21:01.219921365 +0200
> +++ gcc/cp/error.cc	2022-09-13 12:14:31.675733848 +0200
> @@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer
>   {
>     /* 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");
> --- gcc/c-family/c-cppbuiltin.cc.jj	2022-09-13 09:21:00.986924662 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2022-09-13 12:14:31.683733738 +0200
> @@ -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)
>           {
> --- gcc/testsuite/g++.dg/template/error30.C.jj	2020-05-07 23:10:25.242966216 +0200
> +++ gcc/testsuite/g++.dg/template/error30.C	2022-09-13 14:43:49.624224804 +0200
> @@ -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" }
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C.jj	2020-01-14 20:02:46.786609789 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-13 14:45:42.659698626 +0200
> @@ -1,5 +1,5 @@
>   // { dg-do compile { target c++17 } }
>   
>   auto l1 = []() constexpr constexpr { }; // { dg-error "duplicate" }
> -auto l2 = []() mutable mutable { }; // { dg-error "duplicate" }
> -auto l3 = []() static { };	    // { dg-error "static" }
> +auto l2 = []() mutable mutable { }; // { dg-error "conflicting lambda specifiers" }
> +auto l3 = []() static { };	    // { dg-error "static' only valid in lambda with" "" { target c++20_down } }
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-08-26 09:24:12.171614876 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-13 15:05:16.557821712 +0200
> @@ -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
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call1.C.jj	2022-09-13 13:01:34.251875648 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-13 13:00:51.344457645 +0200
> @@ -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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call2.C.jj	2022-09-13 13:01:56.307576484 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-13 13:30:24.092305299 +0200
> @@ -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 "conflicting lambda specifiers" }
> +  auto b = [](int x, int y) static static { return x + y; };		// { dg-error "conflicting lambda specifiers" }
> +									// { 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 "conflicting lambda specifiers" }
> +									// { 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 "conflicting lambda specifiers" }
> +									// { 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 }
> +}
> --- gcc/testsuite/g++.old-deja/g++.jason/operator.C.jj	2020-05-07 23:10:25.242966216 +0200
> +++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-13 14:48:43.219257874 +0200
> @@ -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" }
> 
> 	Jakub
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651]
  2022-09-16 23:23 ` Jason Merrill
@ 2022-09-17  6:42   ` Jakub Jelinek
  2022-09-17 11:30     ` Jason Merrill
  2022-09-19  7:24   ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
  1 sibling, 1 reply; 8+ messages in thread
From: Jakub Jelinek @ 2022-09-17  6:42 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Sat, Sep 17, 2022 at 01:23:59AM +0200, Jason Merrill wrote:
> On 9/13/22 12:42, Jakub Jelinek wrote:
> > 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.  As decl specifier
> > parsing doesn't track about the presence and locations of all specifiers,
> > the patch unfortunately replaces the diagnostics about duplicate mutable
> > with diagnostics about conflicting specifiers because the information
> > whether it was mutable mutable, mutable static, static mutable or static
> > static is lost.
> 
> I wonder why we don't give an error when setting the
> conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should be
> able to give a better diagnostic at that point.

I will try that.

> > Beyond this, 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 I'm least sure about is the call.cc (joust) change, one thing
> > is that we ICEd 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,
> 
> How do you mean?

That is either on the
struct less {
    static constexpr auto operator()(int i, int j) -> bool {
        return i < j;
    }

    using P = bool(*)(int, int);
    operator P() const { return operator(); }
};

static_assert(less{}(1, 2));
testcase from the paper (S in static-operator-call1.C) or any static lambda
which also has static operator() and cast operator.
The paper then talks about
operator()(contrived-parameter, int, int);
call-function(bool(*)(int, int), int, int);
which is exactly what caused the ICE, one of the cand?->fn was the static
operator(), the other cand?->fn isn't a FUNCTION_DECL, but a function
pointer (what the cast operator returns).

> > +	{
> > +	  /* 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.
> > +	     Apply this for C++23 or when the static member function is
> > +	     overloaded call operator (C++23 feature we accept as an
> > +	     extension).  */
> > +	  if ((cxx_dialect >= cxx23
> > +	       || (DECL_OVERLOADED_OPERATOR_P (cand1->fn)
> > +		   && DECL_OVERLOADED_OPERATOR_IS (cand1->fn, CALL_EXPR)))
> > +	      && CONVERSION_RANK (cand2->convs[0]) >= cr_user)
> > +	    winner = -1;
> 
> I don't know what you're trying to do here.  The above passage describes how
> we've always compared static to non-static, which should work the same way
> for operator().

It doesn't work for the static operator() case vs. pointer returned by
cast operator, with just the
-      int static_1 = DECL_STATIC_FUNCTION_P (cand1->fn);
-      int static_2 = DECL_STATIC_FUNCTION_P (cand2->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 (DECL_CONSTRUCTOR_P (cand1->fn)
+      if (TREE_CODE (cand1->fn) == FUNCTION_DECL
+         && TREE_CODE (cand2->fn) == FUNCTION_DECL
+         && DECL_CONSTRUCTOR_P (cand1->fn)
changes in joust so that it doesn't ICE, the call is ambiguous.
Previously the standard had:
"If F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better
nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better
nor worse than ICS1(F); otherwise,"
but that was removed and instead:
"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."
was added elsewhere.  The code implements the former.  We don't have any
conversion for the non-existing object parameter to static, so what I want
to do is keep doing what we were before if the other candidate's conversion
on the first parameter is standard (CONVERSION_RANK (cand2->convs[0]) < cr_user)
and otherwise indicate that the static operator() has there a standard
conversion and so it should be better than any user/ellipsis/bad conversion.
Now, it could be done just for cxx_dialect >= cxx23 as that is pedantically
what the standard says, but if we allow with a pedwarn static operator()
in earlier standards, I think we better treat it there the same, because
otherwise one can't call any static lambdas (all those calls would be
ambiguous).

> > @@ -1135,9 +1141,13 @@ maybe_add_lambda_conv_op (tree type)
> >     tree fn_args = NULL_TREE;
> >     {
> >       int ix = 0;
> > -    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
> > +    tree src = DECL_ARGUMENTS (callop);
> >       tree tgt = NULL;
> > +    if (thisarg)
> > +      src = DECL_CHAIN (src);
> 
> You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.

Ok, will try.

> > +    else if (!decltype_call)
> > +      src = NULL_TREE;
> >       while (src)
> >         {
> >   	tree new_node = copy_node (src);
> > @@ -1160,12 +1170,15 @@ maybe_add_lambda_conv_op (tree type)
> >   	if (generic_lambda_p)
> >   	  {
> >   	    tree a = tgt;
> > -	    if (DECL_PACK_P (tgt))
> > +	    if (thisarg)
> 
> This is wrong, pack expansion handling has nothing to do with 'this'. Only
> the assignment to CALL_EXPR_ARG should depend on thisarg.

The point was that make_pack_expansion etc. is only used for what
is later then stored to CALL_EXPR_ARG.  As we don't need to construct
call at all for the static lambda case, that looked like wasted effort.

> > @@ -1203,14 +1216,18 @@ 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));
> > -  stattype = (cp_build_type_attribute_variant
> > -	      (stattype, TYPE_ATTRIBUTES (optype)));
> > -  if (flag_noexcept_type
> > -      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
> > +  tree stattype
> > +    = build_function_type (fn_result, thisarg ? FUNCTION_ARG_CHAIN (callop)
> > +				      : TYPE_ARG_TYPES (optype));
> > +  stattype = cp_build_type_attribute_variant (stattype,
> > +					      TYPE_ATTRIBUTES (optype));
> > +  if (flag_noexcept_type && TYPE_NOTHROW_P (TREE_TYPE (callop)))
> >       stattype = build_exception_variant (stattype, noexcept_true_spec);
> 
> If !thisarg, shouldn't stattype just be optype?  No need to rebuild it in
> that case.

I thought it should be auto-deduced if it isn't yet.  If it doesn't need to
be, sure, we wouldn't need to bother with decltype_call for !thisarg
either and just not do the loop on arguments etc.
I've tried to create a testcase where I could see the generic static lambda
but I failed to construct one, even with the template <...> lambda syntax
I can't use a template id of the lambda to instantiate some particular case
and then use *.  Would passing a generic lambda to a function with
some function pointer argument trigger it?

	Jakub


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651]
  2022-09-17  6:42   ` Jakub Jelinek
@ 2022-09-17 11:30     ` Jason Merrill
  2022-09-19 12:25       ` [PATCH] c++, v2: " Jakub Jelinek
  0 siblings, 1 reply; 8+ messages in thread
From: Jason Merrill @ 2022-09-17 11:30 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 9/17/22 02:42, Jakub Jelinek wrote:
> On Sat, Sep 17, 2022 at 01:23:59AM +0200, Jason Merrill wrote:
>> On 9/13/22 12:42, Jakub Jelinek wrote:
>>> 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.  As decl specifier
>>> parsing doesn't track about the presence and locations of all specifiers,
>>> the patch unfortunately replaces the diagnostics about duplicate mutable
>>> with diagnostics about conflicting specifiers because the information
>>> whether it was mutable mutable, mutable static, static mutable or static
>>> static is lost.
>>
>> I wonder why we don't give an error when setting the
>> conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should be
>> able to give a better diagnostic at that point.
> 
> I will try that.
> 
>>> Beyond this, 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 I'm least sure about is the call.cc (joust) change, one thing
>>> is that we ICEd 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,
>>
>> How do you mean?
> 
> That is either on the
> struct less {
>      static constexpr auto operator()(int i, int j) -> bool {
>          return i < j;
>      }
> 
>      using P = bool(*)(int, int);
>      operator P() const { return operator(); }
> };
> 
> static_assert(less{}(1, 2));
> testcase from the paper (S in static-operator-call1.C) or any static lambda
> which also has static operator() and cast operator.
> The paper then talks about
> operator()(contrived-parameter, int, int);
> call-function(bool(*)(int, int), int, int);
> which is exactly what caused the ICE, one of the cand?->fn was the static
> operator(), the other cand?->fn isn't a FUNCTION_DECL, but a function
> pointer (what the cast operator returns).

Ah, makes sense.

>>> +	{
>>> +	  /* 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.
>>> +	     Apply this for C++23 or when the static member function is
>>> +	     overloaded call operator (C++23 feature we accept as an
>>> +	     extension).  */
>>> +	  if ((cxx_dialect >= cxx23
>>> +	       || (DECL_OVERLOADED_OPERATOR_P (cand1->fn)
>>> +		   && DECL_OVERLOADED_OPERATOR_IS (cand1->fn, CALL_EXPR)))
>>> +	      && CONVERSION_RANK (cand2->convs[0]) >= cr_user)
>>> +	    winner = -1;
>>
>> I don't know what you're trying to do here.  The above passage describes how
>> we've always compared static to non-static, which should work the same way
>> for operator().
> 
> It doesn't work for the static operator() case vs. pointer returned by
> cast operator, with just the
> -      int static_1 = DECL_STATIC_FUNCTION_P (cand1->fn);
> -      int static_2 = DECL_STATIC_FUNCTION_P (cand2->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 (DECL_CONSTRUCTOR_P (cand1->fn)
> +      if (TREE_CODE (cand1->fn) == FUNCTION_DECL
> +         && TREE_CODE (cand2->fn) == FUNCTION_DECL
> +         && DECL_CONSTRUCTOR_P (cand1->fn)
> changes in joust so that it doesn't ICE, the call is ambiguous.
> Previously the standard had:
> "If F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better
> nor worse than ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better
> nor worse than ICS1(F); otherwise,"
> but that was removed and instead:
> "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."
> was added elsewhere.  The code implements the former.  We don't have any
> conversion for the non-existing object parameter to static, so what I want
> to do is keep doing what we were before if the other candidate's conversion
> on the first parameter is standard (CONVERSION_RANK (cand2->convs[0]) < cr_user)
> and otherwise indicate that the static operator() has there a standard
> conversion and so it should be better than any user/ellipsis/bad conversion.
> Now, it could be done just for cxx_dialect >= cxx23 as that is pedantically
> what the standard says, but if we allow with a pedwarn static operator()
> in earlier standards, I think we better treat it there the same, because
> otherwise one can't call any static lambdas (all those calls would be
> ambiguous).

Ah, OK.  I don't think you need to check for C++23 or CALL_EXPR at all, 
just CONVERSION_RANK of the other candidate conversion.

And I notice that you have winner = -1 in both cases; the first should 
be winner = 1.

>>> @@ -1135,9 +1141,13 @@ maybe_add_lambda_conv_op (tree type)
>>>      tree fn_args = NULL_TREE;
>>>      {
>>>        int ix = 0;
>>> -    tree src = DECL_CHAIN (DECL_ARGUMENTS (callop));
>>> +    tree src = DECL_ARGUMENTS (callop);
>>>        tree tgt = NULL;
>>> +    if (thisarg)
>>> +      src = DECL_CHAIN (src);
>>
>> You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.
> 
> Ok, will try.
> 
>>> +    else if (!decltype_call)
>>> +      src = NULL_TREE;
>>>        while (src)
>>>          {
>>>    	tree new_node = copy_node (src);
>>> @@ -1160,12 +1170,15 @@ maybe_add_lambda_conv_op (tree type)
>>>    	if (generic_lambda_p)
>>>    	  {
>>>    	    tree a = tgt;
>>> -	    if (DECL_PACK_P (tgt))
>>> +	    if (thisarg)
>>
>> This is wrong, pack expansion handling has nothing to do with 'this'. Only
>> the assignment to CALL_EXPR_ARG should depend on thisarg.
> 
> The point was that make_pack_expansion etc. is only used for what
> is later then stored to CALL_EXPR_ARG.  As we don't need to construct
> call at all for the static lambda case, that looked like wasted effort.

Ah, you're right, I thought it was also used for decltype_call.

>>> @@ -1203,14 +1216,18 @@ 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));
>>> -  stattype = (cp_build_type_attribute_variant
>>> -	      (stattype, TYPE_ATTRIBUTES (optype)));
>>> -  if (flag_noexcept_type
>>> -      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
>>> +  tree stattype
>>> +    = build_function_type (fn_result, thisarg ? FUNCTION_ARG_CHAIN (callop)
>>> +				      : TYPE_ARG_TYPES (optype));
>>> +  stattype = cp_build_type_attribute_variant (stattype,
>>> +					      TYPE_ATTRIBUTES (optype));
>>> +  if (flag_noexcept_type && TYPE_NOTHROW_P (TREE_TYPE (callop)))
>>>        stattype = build_exception_variant (stattype, noexcept_true_spec);
>>
>> If !thisarg, shouldn't stattype just be optype?  No need to rebuild it in
>> that case.
> 
> I thought it should be auto-deduced if it isn't yet.

Ah, yes, of course.

You can use FUNCTION_FIRST_USER_PARMTYPE instead of the ?:.

The reformatting of the next two statements seems unnecessary.

> If it doesn't need to
> be, sure, we wouldn't need to bother with decltype_call for !thisarg
> either and just not do the loop on arguments etc.
> I've tried to create a testcase where I could see the generic static lambda
> but I failed to construct one, even with the template <...> lambda syntax
> I can't use a template id of the lambda to instantiate some particular case
> and then use *.  Would passing a generic lambda to a function with
> some function pointer argument trigger it?

How about

void f()
{
   auto l = [] (auto x) static { return x; };
   int (*p)(int) = l;
}

?

Jason


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] c++: Improve diagnostics about conflicting specifiers
  2022-09-16 23:23 ` Jason Merrill
  2022-09-17  6:42   ` Jakub Jelinek
@ 2022-09-19  7:24   ` Jakub Jelinek
  2022-09-27  0:28     ` Jason Merrill
  1 sibling, 1 reply; 8+ messages in thread
From: Jakub Jelinek @ 2022-09-19  7:24 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

On Sat, Sep 17, 2022 at 01:23:59AM +0200, Jason Merrill wrote:
> I wonder why we don't give an error when setting the
> conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should be
> able to give a better diagnostic at that point.

I didn't have time to update the whole patch last night, but this part
seems to be independent and I've managed to test it.

The diagnostics then looks like:
a.C:1:9: error: ‘static’ specifier conflicts with ‘typedef’
    1 | typedef static int a;
      | ~~~~~~~ ^~~~~~
a.C:2:8: error: ‘typedef’ specifier conflicts with ‘static’
    2 | static typedef int b;
      | ~~~~~~ ^~~~~~~
a.C:3:8: error: duplicate ‘static’ specifier
    3 | static static int c;
      | ~~~~~~ ^~~~~~
a.C:4:8: error: ‘extern’ specifier conflicts with ‘static’
    4 | static extern int d;
      | ~~~~~~ ^~~~~~

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-09-19  Jakub Jelinek  <jakub@redhat.com>

gcc/cp/
	* parser.cc (cp_parser_lambda_declarator_opt): Don't diagnose
	conflicting specifiers here.
	(cp_storage_class_name): New variable.
	(cp_parser_decl_specifier_seq): When setting conflicting_specifiers_p
	for the first time, diagnose which exact specifiers conflict.
	(cp_parser_set_storage_class): Likewise.  Move storage_class
	computation earlier.
	* decl.cc (grokdeclarator): Don't diagnose conflicting specifiers
	here, just return error_mark_node.
gcc/testsuite/
	* g++.dg/diagnostic/conflicting-specifiers-1.C: Adjust expected
	diagnostics.
	* g++.dg/parse/typedef8.C: Likewise.
	* g++.dg/parse/crash39.C: Likewise.
	* g++.dg/other/mult-stor1.C: Likewise.
	* g++.dg/cpp2a/constinit3.C: Likewise.

--- gcc/cp/parser.cc.jj	2022-09-16 22:34:28.427708581 +0200
+++ gcc/cp/parser.cc	2022-09-19 09:18:49.561145347 +0200
@@ -11718,9 +11718,6 @@ cp_parser_lambda_declarator_opt (cp_pars
     {
       LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
       quals = TYPE_UNQUALIFIED;
-      if (lambda_specs.conflicting_specifiers_p)
-	error_at (lambda_specs.locations[ds_storage_class],
-		  "duplicate %<mutable%>");
     }
 
   tx_qual = cp_parser_tx_qualifier_opt (parser);
@@ -15720,6 +15717,13 @@ cp_parser_decomposition_declaration (cp_
   return decl;
 }
 
+/* Names of storage classes.  */
+
+static const char *const
+cp_storage_class_name[] = {
+  "", "auto", "register", "static", "extern", "mutable"
+};
+
 /* Parse a decl-specifier-seq.
 
    decl-specifier-seq:
@@ -15941,8 +15945,18 @@ cp_parser_decl_specifier_seq (cp_parser*
 	     may as well commit at this point.  */
 	  cp_parser_commit_to_tentative_parse (parser);
 
-          if (decl_specs->storage_class != sc_none)
-            decl_specs->conflicting_specifiers_p = true;
+	  if (decl_specs->storage_class != sc_none)
+	    {
+	      if (decl_specs->conflicting_specifiers_p)
+		break;
+	      gcc_rich_location richloc (token->location);
+	      location_t oloc = decl_specs->locations[ds_storage_class];
+	      richloc.add_location_if_nearby (oloc);
+	      error_at (&richloc,
+			"%<typedef%> specifier conflicts with %qs",
+			cp_storage_class_name[decl_specs->storage_class]);
+	      decl_specs->conflicting_specifiers_p = true;
+	    }
 	  break;
 
 	  /* storage-class-specifier:
@@ -32826,26 +32840,6 @@ cp_parser_set_storage_class (cp_parser *
 {
   cp_storage_class storage_class;
 
-  if (parser->in_unbraced_linkage_specification_p)
-    {
-      error_at (token->location, "invalid use of %qD in linkage specification",
-		ridpointers[keyword]);
-      return;
-    }
-  else if (decl_specs->storage_class != sc_none)
-    {
-      decl_specs->conflicting_specifiers_p = true;
-      return;
-    }
-
-  if ((keyword == RID_EXTERN || keyword == RID_STATIC)
-      && decl_spec_seq_has_spec_p (decl_specs, ds_thread)
-      && decl_specs->gnu_thread_keyword_p)
-    {
-      pedwarn (decl_specs->locations[ds_thread], 0,
-		"%<__thread%> before %qD", ridpointers[keyword]);
-    }
-
   switch (keyword)
     {
     case RID_AUTO:
@@ -32866,6 +32860,38 @@ cp_parser_set_storage_class (cp_parser *
     default:
       gcc_unreachable ();
     }
+
+  if (parser->in_unbraced_linkage_specification_p)
+    {
+      error_at (token->location, "invalid use of %qD in linkage specification",
+		ridpointers[keyword]);
+      return;
+    }
+  else if (decl_specs->storage_class != sc_none)
+    {
+      if (decl_specs->conflicting_specifiers_p)
+	return;
+      gcc_rich_location richloc (token->location);
+      richloc.add_location_if_nearby (decl_specs->locations[ds_storage_class]);
+      if (decl_specs->storage_class == storage_class)
+	error_at (&richloc, "duplicate %qD specifier", ridpointers[keyword]);
+      else
+	error_at (&richloc,
+		  "%qD specifier conflicts with %qs",
+		  ridpointers[keyword],
+		  cp_storage_class_name[decl_specs->storage_class]);
+      decl_specs->conflicting_specifiers_p = true;
+      return;
+    }
+
+  if ((keyword == RID_EXTERN || keyword == RID_STATIC)
+      && decl_spec_seq_has_spec_p (decl_specs, ds_thread)
+      && decl_specs->gnu_thread_keyword_p)
+    {
+      pedwarn (decl_specs->locations[ds_thread], 0,
+		"%<__thread%> before %qD", ridpointers[keyword]);
+    }
+
   decl_specs->storage_class = storage_class;
   set_and_check_decl_spec_loc (decl_specs, ds_storage_class, token);
 
@@ -32873,8 +32899,16 @@ cp_parser_set_storage_class (cp_parser *
      specifier. If there is a typedef specifier present then set
      conflicting_specifiers_p which will trigger an error later
      on in grokdeclarator. */
-  if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef))
-    decl_specs->conflicting_specifiers_p = true;
+  if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef)
+      && !decl_specs->conflicting_specifiers_p)
+    {
+      gcc_rich_location richloc (token->location);
+      richloc.add_location_if_nearby (decl_specs->locations[ds_typedef]);
+      error_at (&richloc,
+		"%qD specifier conflicts with %<typedef%>",
+		ridpointers[keyword]);
+      decl_specs->conflicting_specifiers_p = true;
+    }
 }
 
 /* Update the DECL_SPECS to reflect the TYPE_SPEC.  If TYPE_DEFINITION_P
--- gcc/cp/decl.cc.jj	2022-09-16 22:34:28.420708676 +0200
+++ gcc/cp/decl.cc	2022-09-18 20:15:13.357162931 +0200
@@ -12089,12 +12089,7 @@ grokdeclarator (const cp_declarator *dec
     }
 
   if (declspecs->conflicting_specifiers_p)
-    {
-      error_at (min_location (declspecs->locations[ds_typedef],
-			      declspecs->locations[ds_storage_class]),
-		"conflicting specifiers in declaration of %qs", name);
-      return error_mark_node;
-    }
+    return error_mark_node;
 
   /* Extract the basic type from the decl-specifier-seq.  */
   type = declspecs->type;
--- gcc/testsuite/g++.dg/diagnostic/conflicting-specifiers-1.C.jj	2020-01-14 20:02:46.815609354 +0100
+++ gcc/testsuite/g++.dg/diagnostic/conflicting-specifiers-1.C	2022-09-18 20:55:53.928964842 +0200
@@ -1 +1 @@
-static typedef int i __attribute__((unused));  // { dg-error "1:conflicting specifiers" }
+static typedef int i __attribute__((unused));  // { dg-error "1:'typedef' specifier conflicts with 'static'" }
--- gcc/testsuite/g++.dg/parse/typedef8.C.jj	2020-01-14 20:02:46.921607767 +0100
+++ gcc/testsuite/g++.dg/parse/typedef8.C	2022-09-18 20:55:23.829375095 +0200
@@ -1,11 +1,11 @@
 //PR c++ 29024
 
-typedef static int a;   // { dg-error "conflicting" }
-typedef register int b; // { dg-error "conflicting" }
-typedef extern int c;   // { dg-error "conflicting" }
-static typedef int a;   // { dg-error "conflicting" }
+typedef static int a;   // { dg-error "'static' specifier conflicts with 'typedef'" }
+typedef register int b; // { dg-error "'register' specifier conflicts with 'typedef'" }
+typedef extern int c;   // { dg-error "'extern' specifier conflicts with 'typedef'" }
+static typedef int a;   // { dg-error "'typedef' specifier conflicts with 'static'" }
 
 void foo()
 {
-  typedef auto int bar; // { dg-error "conflicting|two or more data types" }
+  typedef auto int bar; // { dg-error "'auto' specifier conflicts with 'typedef'|two or more data types" }
 }
--- gcc/testsuite/g++.dg/parse/crash39.C.jj	2020-01-14 20:02:46.911607917 +0100
+++ gcc/testsuite/g++.dg/parse/crash39.C	2022-09-18 20:56:32.004445908 +0200
@@ -1,3 +1,3 @@
 // PR c++/31747
 
-static extern int i; // { dg-error "conflicting specifiers" }
+static extern int i; // { dg-error "'extern' specifier conflicts with 'static'" }
--- gcc/testsuite/g++.dg/other/mult-stor1.C.jj	2020-01-14 20:02:46.902608051 +0100
+++ gcc/testsuite/g++.dg/other/mult-stor1.C	2022-09-18 20:57:43.635469614 +0200
@@ -4,5 +4,5 @@
 
 struct A
 {
-  extern static int i;  // { dg-error "conflicting specifiers" }
+  extern static int i;  // { dg-error "'static' specifier conflicts with 'extern'" }
 };
--- gcc/testsuite/g++.dg/cpp2a/constinit3.C.jj	2020-05-13 21:38:28.356420335 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constinit3.C	2022-09-18 20:57:12.424894986 +0200
@@ -5,7 +5,7 @@ constinit constinit int v1; // { dg-erro
 constexpr constinit int v2 = 1; // { dg-error "can use at most one of the .constinit. and .constexpr. specifiers" }
 constinit constexpr int v3 = 1; // { dg-error "an use at most one of the .constinit. and .constexpr. specifiers" }
 
-extern static constinit int v4; // { dg-error "conflicting specifiers" }
+extern static constinit int v4; // { dg-error "'static' specifier conflicts with 'extern'" }
 extern thread_local constinit int v5;
 extern constinit int v6;
 

	Jakub


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] c++, v2: Implement C++23 P1169R4 - static operator() [PR106651]
  2022-09-17 11:30     ` Jason Merrill
@ 2022-09-19 12:25       ` Jakub Jelinek
  2022-09-27  2:27         ` Jason Merrill
  0 siblings, 1 reply; 8+ messages in thread
From: Jakub Jelinek @ 2022-09-19 12:25 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 22165 bytes --]

Hi!

On Sat, Sep 17, 2022 at 01:30:08PM +0200, Jason Merrill wrote:
Below is an updated patch on top of the
https://gcc.gnu.org/pipermail/gcc-patches/2022-September/601788.html
patch.

> Ah, OK.  I don't think you need to check for C++23 or CALL_EXPR at all, just
> CONVERSION_RANK of the other candidate conversion.

You mean that for C++20 and earlier without static operator () being
involved it is impossible to see non-standard conversion on the first
argument?
I've instrumented GCC during the weekend to log an cases where
static_1 != static_2 and log also whether the corresponding arg on non-static
has standard-conversion or not and the only cases I saw were with
the standard conversion when this static operator () patch wasn't in,
all similar to:
struct S {
  int foo (long);
  static int foo (long long);
};

void
baz ()
{
  S s;
  s.foo (1);
}
and I can't imagine how there could be something other than standard
conversion for the object on which it is called in those cases.

> And I notice that you have winner = -1 in both cases; the first should be
> winner = 1.

Thanks for noticing that, I actually meant to write winner = -1; in the
first and winner = 1; for the second, but that was only because of
misreading it that -1 is when cand1 is better.

> > > You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.
> > 
> > Ok, will try.

Done.

> You can use FUNCTION_FIRST_USER_PARMTYPE instead of the ?:.

Done too.

> The reformatting of the next two statements seems unnecessary.

It is unnecessary (and I left it now out), I was just something
that catched my eye that it could be formatted more nicely.

> How about
> 
> void f()
> {
>   auto l = [] (auto x) static { return x; };
>   int (*p)(int) = l;
> }
> 
> ?

You're right.  Except that we ICE on the above for C++14 and 17 in
tsubst_copy_and_build's
20343		else if (identifier_p (templ))
20344		  {
20345		    /* C++20 P0846: we can encounter an IDENTIFIER_NODE here when
20346		       name lookup found nothing when parsing the template name.  */
20347		    gcc_assert (cxx_dialect >= cxx20 || seen_error ());
20348		    RETURN (tid);
20349		  }
and while for C++20 and 23 we don't, we say
error: cannot convert ‘f()::<lambda(auto:1)>’ to ‘int (*)(int)’ in initialization
so I think it is just that the assertion does nothing for C++20/23 and we
didn't manage to look up templ
 <identifier_node 0x7fffea1fdec0 operator() tree_2 simple-op local bindings <0x7fffea3343e8>>

Just as a wild guess, I've tried the attached incremental patch on top of
the inline patch below and both the ICEs and errors are gone, but whether
that is correct or not sadly no idea.

2022-09-19  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.

--- gcc/cp/cp-tree.h.jj	2022-09-13 18:57:29.356969560 +0200
+++ gcc/cp/cp-tree.h	2022-09-19 11:53:19.780594924 +0200
@@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       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).
@@ -1488,6 +1489,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) \
--- gcc/cp/parser.cc.jj	2022-09-19 11:52:53.140960839 +0200
+++ gcc/cp/parser.cc	2022-09-19 12:11:15.891823798 +0200
@@ -1994,7 +1994,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,
@@ -11719,6 +11719,18 @@ cp_parser_lambda_declarator_opt (cp_pars
       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)
@@ -11804,6 +11816,12 @@ cp_parser_lambda_declarator_opt (cp_pars
     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);
 
@@ -11827,8 +11845,9 @@ cp_parser_lambda_declarator_opt (cp_pars
       {
 	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)
@@ -16022,8 +16041,15 @@ cp_parser_decl_specifier_seq (cp_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);
--- gcc/cp/decl.cc.jj	2022-09-19 11:52:53.169960441 +0200
+++ gcc/cp/decl.cc	2022-09-19 11:53:19.818594402 +0200
@@ -15294,8 +15294,25 @@ grok_op_properties (tree decl, bool comp
      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)
--- gcc/cp/call.cc.jj	2022-09-13 18:57:10.199226101 +0200
+++ gcc/cp/call.cc	2022-09-19 13:28:53.511058032 +0200
@@ -12133,10 +12133,14 @@ joust (struct z_candidate *cand1, struct
   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);
+      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 (DECL_CONSTRUCTOR_P (cand1->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.  */
@@ -12145,9 +12149,20 @@ joust (struct z_candidate *cand1, struct
       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;
 	}
--- gcc/cp/lambda.cc.jj	2022-09-13 18:57:10.267225191 +0200
+++ gcc/cp/lambda.cc	2022-09-19 12:34:46.638522537 +0200
@@ -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,22 @@ 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;
+      int nargs = list_length (DECL_ARGUMENTS (callop));
+      if (thisarg)
+	{
+	  tree instance = cp_build_fold_indirect_ref (thisarg);
+	  objfn = build_min (COMPONENT_REF, NULL_TREE,
+			     instance, objfn, NULL_TREE);
+	  --nargs;
+	  call = prepare_op_call (objfn, nargs);
+	}
 
-      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 +1141,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 +1168,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 +1204,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 +1214,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 +1264,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))
--- gcc/cp/error.cc.jj	2022-09-13 18:57:10.249225432 +0200
+++ gcc/cp/error.cc	2022-09-19 11:53:19.851593949 +0200
@@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer
 {
   /* 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");
--- gcc/c-family/c-cppbuiltin.cc.jj	2022-09-13 18:57:10.128227052 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2022-09-19 11:53:19.873593647 +0200
@@ -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)
         {
--- gcc/testsuite/g++.dg/template/error30.C.jj	2020-07-28 15:39:10.020756063 +0200
+++ gcc/testsuite/g++.dg/template/error30.C	2022-09-19 11:53:19.880593550 +0200
@@ -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" }
--- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C.jj	2020-01-12 11:54:37.127402637 +0100
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-19 12:47:20.398209828 +0200
@@ -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 } }
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-09-13 18:57:10.408223302 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-19 11:53:19.907593180 +0200
@@ -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
--- gcc/testsuite/g++.dg/cpp23/static-operator-call1.C.jj	2022-09-19 11:53:19.908593166 +0200
+++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-19 13:32:10.095362732 +0200
@@ -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);
+}
--- gcc/testsuite/g++.dg/cpp23/static-operator-call2.C.jj	2022-09-19 11:53:19.908593166 +0200
+++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-19 12:54:54.226996543 +0200
@@ -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 }
+}
--- gcc/testsuite/g++.old-deja/g++.jason/operator.C.jj	2020-07-28 15:39:10.024756008 +0200
+++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-19 11:53:19.918593029 +0200
@@ -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" }


	Jakub

[-- Attachment #2: R573 --]
[-- Type: text/plain, Size: 1049 bytes --]

2022-09-19  Jakub Jelinek  <jakub@redhat.com>

	* lambda.cc (maybe_add_lambda_conv_op): If !thisarg, for decltype_call
	use callop rather than objfn in prepare_op_call.

	* g++.dg/cpp23/static-operator-call3.C: New test.

--- gcc/cp/lambda.cc.jj	2022-09-19 12:34:46.638522537 +0200
+++ gcc/cp/lambda.cc	2022-09-19 14:19:31.200378854 +0200
@@ -1122,6 +1122,8 @@ maybe_add_lambda_conv_op (tree type)
 	  --nargs;
 	  call = prepare_op_call (objfn, nargs);
 	}
+      else
+	objfn = callop;
 
       if (type_uses_auto (fn_result))
 	decltype_call = prepare_op_call (objfn, nargs);
--- gcc/testsuite/g++.dg/cpp23/static-operator-call3.C.jj	2022-09-19 13:30:27.850765150 +0200
+++ gcc/testsuite/g++.dg/cpp23/static-operator-call3.C	2022-09-19 13:33:04.178620907 +0200
@@ -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;
+}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++: Improve diagnostics about conflicting specifiers
  2022-09-19  7:24   ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
@ 2022-09-27  0:28     ` Jason Merrill
  0 siblings, 0 replies; 8+ messages in thread
From: Jason Merrill @ 2022-09-27  0:28 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 9/19/22 03:24, Jakub Jelinek wrote:
> Hi!
> 
> On Sat, Sep 17, 2022 at 01:23:59AM +0200, Jason Merrill wrote:
>> I wonder why we don't give an error when setting the
>> conflicting_specifiers_p flag in cp_parser_set_storage_class?  We should be
>> able to give a better diagnostic at that point.
> 
> I didn't have time to update the whole patch last night, but this part
> seems to be independent and I've managed to test it.
> 
> The diagnostics then looks like:
> a.C:1:9: error: ‘static’ specifier conflicts with ‘typedef’
>      1 | typedef static int a;
>        | ~~~~~~~ ^~~~~~
> a.C:2:8: error: ‘typedef’ specifier conflicts with ‘static’
>      2 | static typedef int b;
>        | ~~~~~~ ^~~~~~~
> a.C:3:8: error: duplicate ‘static’ specifier
>      3 | static static int c;
>        | ~~~~~~ ^~~~~~
> a.C:4:8: error: ‘extern’ specifier conflicts with ‘static’
>      4 | static extern int d;
>        | ~~~~~~ ^~~~~~
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2022-09-19  Jakub Jelinek  <jakub@redhat.com>
> 
> gcc/cp/
> 	* parser.cc (cp_parser_lambda_declarator_opt): Don't diagnose
> 	conflicting specifiers here.
> 	(cp_storage_class_name): New variable.
> 	(cp_parser_decl_specifier_seq): When setting conflicting_specifiers_p
> 	for the first time, diagnose which exact specifiers conflict.
> 	(cp_parser_set_storage_class): Likewise.  Move storage_class
> 	computation earlier.
> 	* decl.cc (grokdeclarator): Don't diagnose conflicting specifiers
> 	here, just return error_mark_node.
> gcc/testsuite/
> 	* g++.dg/diagnostic/conflicting-specifiers-1.C: Adjust expected
> 	diagnostics.
> 	* g++.dg/parse/typedef8.C: Likewise.
> 	* g++.dg/parse/crash39.C: Likewise.
> 	* g++.dg/other/mult-stor1.C: Likewise.
> 	* g++.dg/cpp2a/constinit3.C: Likewise.
> 
> --- gcc/cp/parser.cc.jj	2022-09-16 22:34:28.427708581 +0200
> +++ gcc/cp/parser.cc	2022-09-19 09:18:49.561145347 +0200
> @@ -11718,9 +11718,6 @@ cp_parser_lambda_declarator_opt (cp_pars
>       {
>         LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
>         quals = TYPE_UNQUALIFIED;
> -      if (lambda_specs.conflicting_specifiers_p)
> -	error_at (lambda_specs.locations[ds_storage_class],
> -		  "duplicate %<mutable%>");
>       }
>   
>     tx_qual = cp_parser_tx_qualifier_opt (parser);
> @@ -15720,6 +15717,13 @@ cp_parser_decomposition_declaration (cp_
>     return decl;
>   }
>   
> +/* Names of storage classes.  */
> +
> +static const char *const
> +cp_storage_class_name[] = {
> +  "", "auto", "register", "static", "extern", "mutable"
> +};
> +
>   /* Parse a decl-specifier-seq.
>   
>      decl-specifier-seq:
> @@ -15941,8 +15945,18 @@ cp_parser_decl_specifier_seq (cp_parser*
>   	     may as well commit at this point.  */
>   	  cp_parser_commit_to_tentative_parse (parser);
>   
> -          if (decl_specs->storage_class != sc_none)
> -            decl_specs->conflicting_specifiers_p = true;
> +	  if (decl_specs->storage_class != sc_none)
> +	    {
> +	      if (decl_specs->conflicting_specifiers_p)
> +		break;
> +	      gcc_rich_location richloc (token->location);
> +	      location_t oloc = decl_specs->locations[ds_storage_class];
> +	      richloc.add_location_if_nearby (oloc);
> +	      error_at (&richloc,
> +			"%<typedef%> specifier conflicts with %qs",
> +			cp_storage_class_name[decl_specs->storage_class]);
> +	      decl_specs->conflicting_specifiers_p = true;
> +	    }
>   	  break;
>   
>   	  /* storage-class-specifier:
> @@ -32826,26 +32840,6 @@ cp_parser_set_storage_class (cp_parser *
>   {
>     cp_storage_class storage_class;
>   
> -  if (parser->in_unbraced_linkage_specification_p)
> -    {
> -      error_at (token->location, "invalid use of %qD in linkage specification",
> -		ridpointers[keyword]);
> -      return;
> -    }
> -  else if (decl_specs->storage_class != sc_none)
> -    {
> -      decl_specs->conflicting_specifiers_p = true;
> -      return;
> -    }
> -
> -  if ((keyword == RID_EXTERN || keyword == RID_STATIC)
> -      && decl_spec_seq_has_spec_p (decl_specs, ds_thread)
> -      && decl_specs->gnu_thread_keyword_p)
> -    {
> -      pedwarn (decl_specs->locations[ds_thread], 0,
> -		"%<__thread%> before %qD", ridpointers[keyword]);
> -    }
> -
>     switch (keyword)
>       {
>       case RID_AUTO:
> @@ -32866,6 +32860,38 @@ cp_parser_set_storage_class (cp_parser *
>       default:
>         gcc_unreachable ();
>       }
> +
> +  if (parser->in_unbraced_linkage_specification_p)
> +    {
> +      error_at (token->location, "invalid use of %qD in linkage specification",
> +		ridpointers[keyword]);
> +      return;
> +    }
> +  else if (decl_specs->storage_class != sc_none)
> +    {
> +      if (decl_specs->conflicting_specifiers_p)
> +	return;
> +      gcc_rich_location richloc (token->location);
> +      richloc.add_location_if_nearby (decl_specs->locations[ds_storage_class]);
> +      if (decl_specs->storage_class == storage_class)
> +	error_at (&richloc, "duplicate %qD specifier", ridpointers[keyword]);
> +      else
> +	error_at (&richloc,
> +		  "%qD specifier conflicts with %qs",
> +		  ridpointers[keyword],
> +		  cp_storage_class_name[decl_specs->storage_class]);
> +      decl_specs->conflicting_specifiers_p = true;
> +      return;
> +    }
> +
> +  if ((keyword == RID_EXTERN || keyword == RID_STATIC)
> +      && decl_spec_seq_has_spec_p (decl_specs, ds_thread)
> +      && decl_specs->gnu_thread_keyword_p)
> +    {
> +      pedwarn (decl_specs->locations[ds_thread], 0,
> +		"%<__thread%> before %qD", ridpointers[keyword]);
> +    }
> +
>     decl_specs->storage_class = storage_class;
>     set_and_check_decl_spec_loc (decl_specs, ds_storage_class, token);
>   
> @@ -32873,8 +32899,16 @@ cp_parser_set_storage_class (cp_parser *
>        specifier. If there is a typedef specifier present then set
>        conflicting_specifiers_p which will trigger an error later
>        on in grokdeclarator. */
> -  if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef))
> -    decl_specs->conflicting_specifiers_p = true;
> +  if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef)
> +      && !decl_specs->conflicting_specifiers_p)
> +    {
> +      gcc_rich_location richloc (token->location);
> +      richloc.add_location_if_nearby (decl_specs->locations[ds_typedef]);
> +      error_at (&richloc,
> +		"%qD specifier conflicts with %<typedef%>",
> +		ridpointers[keyword]);
> +      decl_specs->conflicting_specifiers_p = true;
> +    }
>   }
>   
>   /* Update the DECL_SPECS to reflect the TYPE_SPEC.  If TYPE_DEFINITION_P
> --- gcc/cp/decl.cc.jj	2022-09-16 22:34:28.420708676 +0200
> +++ gcc/cp/decl.cc	2022-09-18 20:15:13.357162931 +0200
> @@ -12089,12 +12089,7 @@ grokdeclarator (const cp_declarator *dec
>       }
>   
>     if (declspecs->conflicting_specifiers_p)
> -    {
> -      error_at (min_location (declspecs->locations[ds_typedef],
> -			      declspecs->locations[ds_storage_class]),
> -		"conflicting specifiers in declaration of %qs", name);
> -      return error_mark_node;
> -    }
> +    return error_mark_node;
>   
>     /* Extract the basic type from the decl-specifier-seq.  */
>     type = declspecs->type;
> --- gcc/testsuite/g++.dg/diagnostic/conflicting-specifiers-1.C.jj	2020-01-14 20:02:46.815609354 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/conflicting-specifiers-1.C	2022-09-18 20:55:53.928964842 +0200
> @@ -1 +1 @@
> -static typedef int i __attribute__((unused));  // { dg-error "1:conflicting specifiers" }
> +static typedef int i __attribute__((unused));  // { dg-error "1:'typedef' specifier conflicts with 'static'" }
> --- gcc/testsuite/g++.dg/parse/typedef8.C.jj	2020-01-14 20:02:46.921607767 +0100
> +++ gcc/testsuite/g++.dg/parse/typedef8.C	2022-09-18 20:55:23.829375095 +0200
> @@ -1,11 +1,11 @@
>   //PR c++ 29024
>   
> -typedef static int a;   // { dg-error "conflicting" }
> -typedef register int b; // { dg-error "conflicting" }
> -typedef extern int c;   // { dg-error "conflicting" }
> -static typedef int a;   // { dg-error "conflicting" }
> +typedef static int a;   // { dg-error "'static' specifier conflicts with 'typedef'" }
> +typedef register int b; // { dg-error "'register' specifier conflicts with 'typedef'" }
> +typedef extern int c;   // { dg-error "'extern' specifier conflicts with 'typedef'" }
> +static typedef int a;   // { dg-error "'typedef' specifier conflicts with 'static'" }
>   
>   void foo()
>   {
> -  typedef auto int bar; // { dg-error "conflicting|two or more data types" }
> +  typedef auto int bar; // { dg-error "'auto' specifier conflicts with 'typedef'|two or more data types" }
>   }
> --- gcc/testsuite/g++.dg/parse/crash39.C.jj	2020-01-14 20:02:46.911607917 +0100
> +++ gcc/testsuite/g++.dg/parse/crash39.C	2022-09-18 20:56:32.004445908 +0200
> @@ -1,3 +1,3 @@
>   // PR c++/31747
>   
> -static extern int i; // { dg-error "conflicting specifiers" }
> +static extern int i; // { dg-error "'extern' specifier conflicts with 'static'" }
> --- gcc/testsuite/g++.dg/other/mult-stor1.C.jj	2020-01-14 20:02:46.902608051 +0100
> +++ gcc/testsuite/g++.dg/other/mult-stor1.C	2022-09-18 20:57:43.635469614 +0200
> @@ -4,5 +4,5 @@
>   
>   struct A
>   {
> -  extern static int i;  // { dg-error "conflicting specifiers" }
> +  extern static int i;  // { dg-error "'static' specifier conflicts with 'extern'" }
>   };
> --- gcc/testsuite/g++.dg/cpp2a/constinit3.C.jj	2020-05-13 21:38:28.356420335 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constinit3.C	2022-09-18 20:57:12.424894986 +0200
> @@ -5,7 +5,7 @@ constinit constinit int v1; // { dg-erro
>   constexpr constinit int v2 = 1; // { dg-error "can use at most one of the .constinit. and .constexpr. specifiers" }
>   constinit constexpr int v3 = 1; // { dg-error "an use at most one of the .constinit. and .constexpr. specifiers" }
>   
> -extern static constinit int v4; // { dg-error "conflicting specifiers" }
> +extern static constinit int v4; // { dg-error "'static' specifier conflicts with 'extern'" }
>   extern thread_local constinit int v5;
>   extern constinit int v6;
>   
> 
> 	Jakub
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] c++, v2: Implement C++23 P1169R4 - static operator() [PR106651]
  2022-09-19 12:25       ` [PATCH] c++, v2: " Jakub Jelinek
@ 2022-09-27  2:27         ` Jason Merrill
  0 siblings, 0 replies; 8+ messages in thread
From: Jason Merrill @ 2022-09-27  2:27 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 9/19/22 08:25, Jakub Jelinek wrote:
> Hi!
> 
> On Sat, Sep 17, 2022 at 01:30:08PM +0200, Jason Merrill wrote:
> Below is an updated patch on top of the
> https://gcc.gnu.org/pipermail/gcc-patches/2022-September/601788.html
> patch.
> 
>> Ah, OK.  I don't think you need to check for C++23 or CALL_EXPR at all, just
>> CONVERSION_RANK of the other candidate conversion.
> 
> You mean that for C++20 and earlier without static operator () being
> involved it is impossible to see non-standard conversion on the first
> argument?

I believe so, and even if it were possible the change is correct for 
earlier standards.

> I've instrumented GCC during the weekend to log an cases where
> static_1 != static_2 and log also whether the corresponding arg on non-static
> has standard-conversion or not and the only cases I saw were with
> the standard conversion when this static operator () patch wasn't in,
> all similar to:
> struct S {
>    int foo (long);
>    static int foo (long long);
> };
> 
> void
> baz ()
> {
>    S s;
>    s.foo (1);
> }
> and I can't imagine how there could be something other than standard
> conversion for the object on which it is called in those cases.
> 
>> And I notice that you have winner = -1 in both cases; the first should be
>> winner = 1.
> 
> Thanks for noticing that, I actually meant to write winner = -1; in the
> first and winner = 1; for the second, but that was only because of
> misreading it that -1 is when cand1 is better.
> 
>>>> You could use FUNCTION_FIRST_USER_PARM to skip 'this' if present.
>>>
>>> Ok, will try.
> 
> Done.
> 
>> You can use FUNCTION_FIRST_USER_PARMTYPE instead of the ?:.
> 
> Done too.
> 
>> The reformatting of the next two statements seems unnecessary.
> 
> It is unnecessary (and I left it now out), I was just something
> that catched my eye that it could be formatted more nicely.
> 
>> How about
>>
>> void f()
>> {
>>    auto l = [] (auto x) static { return x; };
>>    int (*p)(int) = l;
>> }
>>
>> ?
> 
> You're right.  Except that we ICE on the above for C++14 and 17 in
> tsubst_copy_and_build's
> 20343		else if (identifier_p (templ))
> 20344		  {
> 20345		    /* C++20 P0846: we can encounter an IDENTIFIER_NODE here when
> 20346		       name lookup found nothing when parsing the template name.  */
> 20347		    gcc_assert (cxx_dialect >= cxx20 || seen_error ());
> 20348		    RETURN (tid);
> 20349		  }
> and while for C++20 and 23 we don't, we say
> error: cannot convert ‘f()::<lambda(auto:1)>’ to ‘int (*)(int)’ in initialization
> so I think it is just that the assertion does nothing for C++20/23 and we
> didn't manage to look up templ
>   <identifier_node 0x7fffea1fdec0 operator() tree_2 simple-op local bindings <0x7fffea3343e8>>
> 
> Just as a wild guess, I've tried the attached incremental patch on top of
> the inline patch below and both the ICEs and errors are gone, but whether
> that is correct or not sadly no idea.

That makes sense.  The patch with followup is OK.

> 2022-09-19  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.
> 
> --- gcc/cp/cp-tree.h.jj	2022-09-13 18:57:29.356969560 +0200
> +++ gcc/cp/cp-tree.h	2022-09-19 11:53:19.780594924 +0200
> @@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI
>         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).
> @@ -1488,6 +1489,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) \
> --- gcc/cp/parser.cc.jj	2022-09-19 11:52:53.140960839 +0200
> +++ gcc/cp/parser.cc	2022-09-19 12:11:15.891823798 +0200
> @@ -1994,7 +1994,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,
> @@ -11719,6 +11719,18 @@ cp_parser_lambda_declarator_opt (cp_pars
>         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)
> @@ -11804,6 +11816,12 @@ cp_parser_lambda_declarator_opt (cp_pars
>       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);
>   
> @@ -11827,8 +11845,9 @@ cp_parser_lambda_declarator_opt (cp_pars
>         {
>   	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)
> @@ -16022,8 +16041,15 @@ cp_parser_decl_specifier_seq (cp_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);
> --- gcc/cp/decl.cc.jj	2022-09-19 11:52:53.169960441 +0200
> +++ gcc/cp/decl.cc	2022-09-19 11:53:19.818594402 +0200
> @@ -15294,8 +15294,25 @@ grok_op_properties (tree decl, bool comp
>        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)
> --- gcc/cp/call.cc.jj	2022-09-13 18:57:10.199226101 +0200
> +++ gcc/cp/call.cc	2022-09-19 13:28:53.511058032 +0200
> @@ -12133,10 +12133,14 @@ joust (struct z_candidate *cand1, struct
>     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);
> +      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 (DECL_CONSTRUCTOR_P (cand1->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.  */
> @@ -12145,9 +12149,20 @@ joust (struct z_candidate *cand1, struct
>         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;
>   	}
> --- gcc/cp/lambda.cc.jj	2022-09-13 18:57:10.267225191 +0200
> +++ gcc/cp/lambda.cc	2022-09-19 12:34:46.638522537 +0200
> @@ -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,22 @@ 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;
> +      int nargs = list_length (DECL_ARGUMENTS (callop));
> +      if (thisarg)
> +	{
> +	  tree instance = cp_build_fold_indirect_ref (thisarg);
> +	  objfn = build_min (COMPONENT_REF, NULL_TREE,
> +			     instance, objfn, NULL_TREE);
> +	  --nargs;
> +	  call = prepare_op_call (objfn, nargs);
> +	}
>   
> -      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 +1141,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 +1168,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 +1204,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 +1214,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 +1264,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))
> --- gcc/cp/error.cc.jj	2022-09-13 18:57:10.249225432 +0200
> +++ gcc/cp/error.cc	2022-09-19 11:53:19.851593949 +0200
> @@ -1692,7 +1692,13 @@ dump_lambda_function (cxx_pretty_printer
>   {
>     /* 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");
> --- gcc/c-family/c-cppbuiltin.cc.jj	2022-09-13 18:57:10.128227052 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2022-09-19 11:53:19.873593647 +0200
> @@ -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)
>           {
> --- gcc/testsuite/g++.dg/template/error30.C.jj	2020-07-28 15:39:10.020756063 +0200
> +++ gcc/testsuite/g++.dg/template/error30.C	2022-09-19 11:53:19.880593550 +0200
> @@ -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" }
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C.jj	2020-01-12 11:54:37.127402637 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-lambda13.C	2022-09-19 12:47:20.398209828 +0200
> @@ -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 } }
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2022-09-13 18:57:10.408223302 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2022-09-19 11:53:19.907593180 +0200
> @@ -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
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call1.C.jj	2022-09-19 11:53:19.908593166 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call1.C	2022-09-19 13:32:10.095362732 +0200
> @@ -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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/static-operator-call2.C.jj	2022-09-19 11:53:19.908593166 +0200
> +++ gcc/testsuite/g++.dg/cpp23/static-operator-call2.C	2022-09-19 12:54:54.226996543 +0200
> @@ -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 }
> +}
> --- gcc/testsuite/g++.old-deja/g++.jason/operator.C.jj	2020-07-28 15:39:10.024756008 +0200
> +++ gcc/testsuite/g++.old-deja/g++.jason/operator.C	2022-09-19 11:53:19.918593029 +0200
> @@ -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" }
> 
> 
> 	Jakub


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-09-27  2:27 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-13 16:42 [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651] Jakub Jelinek
2022-09-16 23:23 ` Jason Merrill
2022-09-17  6:42   ` Jakub Jelinek
2022-09-17 11:30     ` Jason Merrill
2022-09-19 12:25       ` [PATCH] c++, v2: " Jakub Jelinek
2022-09-27  2:27         ` Jason Merrill
2022-09-19  7:24   ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
2022-09-27  0:28     ` Jason Merrill

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