2022-09-19 Jakub Jelinek <ja...@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