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)
{
len = cand1->num_convs;
if (len != cand2->num_convs)
{
- int static_1 = DECL_STATIC_FUNCTION_P (cand1->fn);
- int static_2 = DECL_STATIC_FUNCTION_P (cand2->fn);
-
- if (DECL_CONSTRUCTOR_P (cand1->fn)
+ int static_1 = (TREE_CODE (cand1->fn) == FUNCTION_DECL
+ && DECL_STATIC_FUNCTION_P (cand1->fn));
+ int static_2 = (TREE_CODE (cand2->fn) == FUNCTION_DECL
+ && DECL_STATIC_FUNCTION_P (cand2->fn));
+
+ if (TREE_CODE (cand1->fn) == FUNCTION_DECL
+ && TREE_CODE (cand2->fn) == FUNCTION_DECL
+ && DECL_CONSTRUCTOR_P (cand1->fn)
&& is_list_ctor (cand1->fn) != is_list_ctor (cand2->fn))
/* We're comparing a near-match list constructor and a near-match
non-list constructor. Just treat them as unordered. */
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;
}
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).
#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) \
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)
{
/* 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");
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;
return expression for a deduced return call op to allow for simple
implementation of the conversion operator. */
- tree instance = cp_build_fold_indirect_ref (thisarg);
- tree objfn = lookup_template_function (DECL_NAME (callop),
- DECL_TI_ARGS (callop));
- objfn = build_min (COMPONENT_REF, NULL_TREE,
- instance, objfn, NULL_TREE);
- int nargs = list_length (DECL_ARGUMENTS (callop)) - 1;
+ tree objfn;
+ int nargs = list_length (DECL_ARGUMENTS (callop));
+ if (thisarg)
+ {
+ tree instance = cp_build_fold_indirect_ref (thisarg);
+ objfn = lookup_template_function (DECL_NAME (callop),
+ DECL_TI_ARGS (callop));
+ objfn = build_min (COMPONENT_REF, NULL_TREE,
+ instance, objfn, NULL_TREE);
+ --nargs;
+ call = prepare_op_call (objfn, nargs);
+ }
+ else
+ objfn = callop;
- call = prepare_op_call (objfn, nargs);
if (type_uses_auto (fn_result))
decltype_call = prepare_op_call (objfn, nargs);
}
- else
+ else if (thisarg)
{
direct_argvec = make_tree_vector ();
direct_argvec->quick_push (thisarg);
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);
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)
{
tf_warning_or_error);
}
}
- else
+ else if (thisarg)
{
/* Don't warn on deprecated or unavailable lambda declarations, unless
the lambda is actually called. */
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
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))
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,
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)
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);
{
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)
&& 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);
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 } }
#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
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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 }
+}
--- /dev/null
+// 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;
+}
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" }
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" }