From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 79B623857C71 for ; Fri, 16 Sep 2022 23:24:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 79B623857C71 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1663370645; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=A/dvS0KRcAJN9Q4nDnbt2orWLRUTXCEqgcyoxQ0kP3w=; b=QdmeGNu1W5LFvB4roRaWXrrzlJ7N/hqbro3XW4sFSbBrFmrw5tyn9BXlum6x5HuBOQ2APU YB8u3UCHcU7/ZwdXmA/XxEKKY2Kjywnj6FWnK/67XCi8DMMoOoMA9niBb3aXcml6a7jWv9 npDiH2N0LQtHDYaqQByM06kLpTDzerY= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-329-wfROG3f2O32tkDaqtUUIfA-1; Fri, 16 Sep 2022 19:24:03 -0400 X-MC-Unique: wfROG3f2O32tkDaqtUUIfA-1 Received: by mail-wm1-f71.google.com with SMTP id ay29-20020a05600c1e1d00b003b49a9f987cso612117wmb.8 for ; Fri, 16 Sep 2022 16:24:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date; bh=A/dvS0KRcAJN9Q4nDnbt2orWLRUTXCEqgcyoxQ0kP3w=; b=kMHR0awI/wpPOgJhaxgA9gAyN7LQX7R0Fgutdk+T+28v4Uu8FpU8UTvSTQzFei/d7/ Oe0VNLLwZFxd9S1pw6yRcnoYF/tFF/FyjLsIXWqtCwp5PWFKTy7UoKAtJmOG3uvoxu8p D1tww3rGlDEzDG2yMHBWelMObEdw4vUQqgqz+9yh59sK6yPe3BJ/f98X83q2Tj57StDu olnZfrj+T/e+dsVQ1NOGyemmpE+bcg2fPSmxBTtuJpmIcCdtpjEAYFPrfctYsc0X6GrN 2Ts73mkV1SXwsQxj/km59EcXJyrUXoSCCoyZJ4mNl+S6QQe9JlR9Kc2AFnnl4dzFhZPM A+Jw== X-Gm-Message-State: ACrzQf3W+uXOCPxGqR1vDVrYpnm8hlydikzyP8QyCCpSg7dKUsQnPTNI nMhpErM8pZIqdxZqB4ztFCyh63AWBtFCaa2JoCkUHRxrlBLKuOk54nfDSZlt53L5vo23NjUHumo U4jutZyUYKWMswF+THQ== X-Received: by 2002:a05:6000:1007:b0:22a:38fc:aeb2 with SMTP id a7-20020a056000100700b0022a38fcaeb2mr4384369wrx.297.1663370642371; Fri, 16 Sep 2022 16:24:02 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6OLezKULw4ZxHJS114DBLvh6UhG2VM+nckNXhxDVMIQw/rIF/TyPy/LSFzcev1asgH8vmEZw== X-Received: by 2002:a05:6000:1007:b0:22a:38fc:aeb2 with SMTP id a7-20020a056000100700b0022a38fcaeb2mr4384357wrx.297.1663370641789; Fri, 16 Sep 2022 16:24:01 -0700 (PDT) Received: from [192.168.11.63] ([62.209.192.211]) by smtp.gmail.com with ESMTPSA id x12-20020a05600c2d0c00b003b47575d304sm4461957wmf.32.2022.09.16.16.24.00 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 16 Sep 2022 16:24:01 -0700 (PDT) Message-ID: <35c635ad-118f-63bd-eb58-cd949286e28d@redhat.com> Date: Sat, 17 Sep 2022 01:23:59 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.13.0 Subject: Re: [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651] To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org References: From: Jason Merrill In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NICE_REPLY_A,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 > > 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], > + "% 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 %"); > + "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 struct A; > > -template class B> A::x> operator() (); // { dg-error "51:.A::x> operator\\(\\)\\(\\). must be a non-static member function" } > +template class B> A::x> operator() (); // { dg-error "51:.A::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 > +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 {} (1, 2), ""); > + > +template > +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 = [](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 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 >