From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v2: Implement C++23 P1169R4 - static operator() [PR106651]
Date: Mon, 26 Sep 2022 22:27:34 -0400 [thread overview]
Message-ID: <4f3f00fb-e4ec-5d7f-ff8f-2445abd314cd@redhat.com> (raw)
In-Reply-To: <Yyhf0bMfpvB1cz/a@tucnak>
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
next prev parent reply other threads:[~2022-09-27 2:27 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-13 16:42 [PATCH] c++: " 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 [this message]
2022-09-19 7:24 ` [PATCH] c++: Improve diagnostics about conflicting specifiers Jakub Jelinek
2022-09-27 0:28 ` Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4f3f00fb-e4ec-5d7f-ff8f-2445abd314cd@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).