From patchwork Tue Sep 13 16:42:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1186 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:5044:0:0:0:0:0 with SMTP id h4csp2437275wrt; Tue, 13 Sep 2022 09:43:22 -0700 (PDT) X-Google-Smtp-Source: AA6agR6fdT0oSWmrPyphwQuy0sqZSA0YnDktcTMq3yYDUpNjVwZX70zrVaBTyIgukzaNy5b/1Qr1 X-Received: by 2002:a17:907:7f04:b0:77d:5bad:46d7 with SMTP id qf4-20020a1709077f0400b0077d5bad46d7mr7494103ejc.663.1663087401997; Tue, 13 Sep 2022 09:43:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1663087401; cv=none; d=google.com; s=arc-20160816; b=KbjbJAzgRuJzWxLr3js8BAKtAebcjWBi3GuggHrAzsMZVGvkDh1Hwy1drj3L/7lyBZ mAtxVqeyLf4G9xPft+V92PsLol+jQOSnRvQ9RsBJKq4UU+5Qhrz2wV5FY/DhHp9OftfW 34kAB90zYfomsMfKQP13SFUq82dDznFrSELRSZY/2KKCAn88yp5mtzSppHhv2LFEYZbU U9yBw/VpZkWuMk/xMXPWvr8CEfL36DWWspv0nD/NTLYQxyCf5VHTx9XJzI4vNkb4zBVe naQW9O4o2sEU2iZfsrnCQEWpLYyFS/Jo3IHdW/nZABFmEJibShfiDiLgtAIdgr7/zDEB UxIA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:from:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence :content-disposition:mime-version:message-id:subject:to:date :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=BPaeKBacDAolD2eOwi0H+GMBpByDIRPerIYFlwbYiZM=; b=BcyF/fpnqq5f0cJA5HML3HGodNqGYyvN7V0iVpY7jlThcDyi2OT+H3t6A5t7bhYkBc ol0KNcOt7OQYTPdhsK1f7WbjJHVu8JnFDKCIio8EXOdQmSJrANGs7sElDM7ZGnISgjjB pmJ11Mp4znABPhe/pa9ztfEy5yhktGdlkht2E95WHaKNIUvkxeUMwtVB1yQywRzwH2eJ S8j0SwuKmhuqB6HRrFBJIi/qotNVxG6s5AMqjUuTPqYMx+JU+D+U45hq4gKfY8fXCWFi Tlq4DsevyXVffvw90WBQbwVt2q+Xyhs2JRQTSH67omdf6WbXexT5dyWhg/5cUx6TQTA2 fauQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=qA6HzTqL; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id nc29-20020a1709071c1d00b0077a2432f257si10737344ejc.696.2022.09.13.09.43.21 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Sep 2022 09:43:21 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=qA6HzTqL; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A3D3D385624D for ; Tue, 13 Sep 2022 16:43:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A3D3D385624D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1663087400; bh=BPaeKBacDAolD2eOwi0H+GMBpByDIRPerIYFlwbYiZM=; h=Date:To:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=qA6HzTqLYDzs1TBx5Wag9gzSS9YF1qVznhEq3jP8Ecdi8EJrQE5Mr6cZuGCnhNRtt 13s5RGGEEL0p5ZZk8cEWUc1t9gs0+cKhfCwBTFODgTa0zYN3uxn23hbvIna4evse6p jl9hQ3ax92yFigELOTE5TNyeHG6LVA/jDsLpbZEY= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 2A3063858D1E for ; Tue, 13 Sep 2022 16:42:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2A3063858D1E Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-614-bcX-0KrvP0qeix2W1Y3A3A-1; Tue, 13 Sep 2022 12:42:32 -0400 X-MC-Unique: bcX-0KrvP0qeix2W1Y3A3A-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6714F38041C7 for ; Tue, 13 Sep 2022 16:42:32 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.192.41]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 06990492B04; Tue, 13 Sep 2022 16:42:31 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 28DGgTsK150448 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 13 Sep 2022 18:42:30 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 28DGgTMc150447; Tue, 13 Sep 2022 18:42:29 +0200 Date: Tue, 13 Sep 2022 18:42:28 +0200 To: Jason Merrill Subject: [PATCH] c++: Implement C++23 P1169R4 - static operator() [PR106651] Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-4.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1743873535920520579?= X-GMAIL-MSGID: =?utf-8?q?1743873535920520579?= 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 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. Jakub --- 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; + 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 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" }