From patchwork Tue Oct 19 12:00:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 46388 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 16A853858006 for ; Tue, 19 Oct 2021 12:02:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 16A853858006 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1634644972; bh=ZWvbkgXQ29YvNBHXch+msq3emiEKAWyePvr+lgJH2BM=; h=Date:To:Subject:References:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=SzD4xye0QBcmgpE7AKnIrOcKpzJBBtzH9pofspBcEeyVBNyBzqpoG2SDi+XrrX0AH 6Y7OR7k172/d/FKyFcaGo7xO6eVqLupe05sCgL1St4My9neLg7N8Ct4elsvmMpkSat /S9DxY7uqOB1QRlbtdt0L5agy2NWAOr3vpf/YiSs= 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 [216.205.24.124]) by sourceware.org (Postfix) with ESMTPS id 3B9EA3858006 for ; Tue, 19 Oct 2021 12:00:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 3B9EA3858006 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-95-z-HBrteYOPmcCCHcfAZGlw-1; Tue, 19 Oct 2021 08:00:37 -0400 X-MC-Unique: z-HBrteYOPmcCCHcfAZGlw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6801810A8E3B for ; Tue, 19 Oct 2021 12:00:36 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.193.172]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CA9AC78325; Tue, 19 Oct 2021 12:00:24 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.16.1/8.16.1) with ESMTPS id 19JC0L952922645 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 19 Oct 2021 14:00:22 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.16.1/8.16.1/Submit) id 19JC0LWJ2922644; Tue, 19 Oct 2021 14:00:21 +0200 Date: Tue, 19 Oct 2021 14:00:21 +0200 To: Jason Merrill Subject: [PATCH, v2] c++: Diagnose taking address of an immediate member function [PR102753] Message-ID: <20211019120021.GL304296@tucnak> References: <20211018081258.GV304296@tucnak> <40b79f8d-dd22-a2f4-f63d-802d67395444@redhat.com> MIME-Version: 1.0 In-Reply-To: <40b79f8d-dd22-a2f4-f63d-802d67395444@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-5.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" On Mon, Oct 18, 2021 at 12:42:00PM -0400, Jason Merrill wrote: > > --- gcc/cp/typeck.c.jj 2021-10-05 09:53:55.382734051 +0200 > > +++ gcc/cp/typeck.c 2021-10-15 19:28:38.034213437 +0200 > > @@ -6773,9 +6773,21 @@ cp_build_addr_expr_1 (tree arg, bool str > > return error_mark_node; > > } > > + if (TREE_CODE (t) == FUNCTION_DECL > > + && DECL_IMMEDIATE_FUNCTION_P (t) > > + && cp_unevaluated_operand == 0 > > + && (current_function_decl == NULL_TREE > > + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) > > This doesn't cover some of the other cases of immediate context; we should > probably factor most of immediate_invocation_p out into a function called > something like in_immediate_context and use it here, and in several other > places as well. You're right, I've done that for the two spots in cp_build_addr_expr_1 and added testsuite coverage for where it changed behavior. While doing that I've discovered further issues. One is that we weren't diagnosing PMFs referring to immediate methods returned from immediate functions (either directly or embedded in aggregates). I'm not sure if it can only appear as PTRMEM_CST which I've handled (cp_walk_subtree only walks the type and not the PTRMEM_CST_MEMBER) or something else. Another issue is that while default arg in immediate function containing &immediate_fn works properly, if it is immediate_fn instead, we were incorrectly rejecting it. I've handled this in build_over_call, though with this usage in_consteval_if_p is slightly misnamed, it stands for in consteval if or some other reason why we are currently in immediate function context. Though, that flag alone can't be all the reasons for being in immediate function contexts, as I've tried the other reasons can't be handled in such a bool and need to be tested too. And another thing isn't in a patch, but I'm wondering whether we don't handle it incorrectly. constexpr.c has: /* Check that immediate invocation does not return an expression referencing any immediate function decls. They need to be allowed while parsing immediate functions, but can't leak outside of them. */ if (is_consteval && t != r && (current_function_decl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) as condition for the discovery of embedded immediate FUNCTION_DECLs (or now PTRMEM_CSTs). If I remove the && (current... ..._decl)) then g++.dg/cpp2a/consteval7.C's struct S { int b; int (*c) (); }; consteval S baz () { return { 5, foo }; } consteval int qux () { S s = baz (); return s.b + s.c (); } consteval int quux () { constexpr S s = baz (); return s.b + s.c (); } quux line fails, but based on http://eel.is/c++draft/expr.const#11 I wonder if it shouldn't fail (clang++ -std=c++20 rejects it), and be only accepted without the constexpr keyword before S s. Also wonder about e.g. consteval int foo () { return 42; } consteval int bar () { auto fn1 = foo; // This must be ok constexpr auto fn2 = foo; // Isn't this an error? return fn1 () + fn2 (); } constexpr int baz () { if consteval { auto fn1 = foo; // This must be ok constexpr auto fn2 = foo; // Isn't this an error? return fn1 () + fn2 (); } return 0; } auto a = bar (); static_assert (bar () == 84); static_assert (baz () == 84); (again, clang++ -std=c++20 rejects the fn2 = foo; case, but doesn't implement consteval if, so can't test the other one). For taking address of an immediate function or method if it is taken outside of immediate function context we already have diagnostics about it, but shouldn't the immediate FUNCTION_DECL discovery in cxx_eval_outermost_constant_expression be instead guarded with something like if (is_consteval || in_immediate_context ()) and be done regardless of whether t != r? 2021-10-19 Jakub Jelinek PR c++/102753 * cp-tree.h (in_immediate_context): Declare. * call.c (in_immediate_context): New function. (immediate_invocation_p): Use it. (build_over_call): Temporarily set in_consteval_if_p for convert_default_arg calls of immediate invocations. * typeck.c (cp_build_addr_expr_1): Diagnose taking address of an immediate method. Use t instead of TREE_OPERAND (arg, 1). Use in_immediate_context function. * constexpr.c (find_immediate_fndecl): Handle PTRMEM_CST which refers to immediate function decl. * g++.dg/cpp2a/consteval20.C: New test. * g++.dg/cpp2a/consteval21.C: New test. * g++.dg/cpp2a/consteval22.C: New test. * g++.dg/cpp2a/consteval23.C: New test. * g++.dg/cpp23/consteval-if11.C: New test. Jakub --- gcc/cp/cp-tree.h.jj 2021-10-15 11:58:44.968133548 +0200 +++ gcc/cp/cp-tree.h 2021-10-19 10:40:58.375799274 +0200 @@ -6547,6 +6547,7 @@ extern tree perform_direct_initializatio tsubst_flags_t); extern vec *resolve_args (vec*, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); +extern bool in_immediate_context (); extern tree build_cxx_call (tree, int, tree *, tsubst_flags_t, tree = NULL_TREE); --- gcc/cp/call.c.jj 2021-10-15 11:58:44.947133850 +0200 +++ gcc/cp/call.c 2021-10-19 13:04:42.333421774 +0200 @@ -9025,6 +9025,19 @@ build_trivial_dtor_call (tree instance, instance, clobber); } +/* Return true if in an immediate function context. */ + +bool +in_immediate_context () +{ + return (cp_unevaluated_operand != 0 + || (current_function_decl != NULL_TREE + && DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) + || (current_binding_level->kind == sk_function_parms + && current_binding_level->immediate_fn_ctx_p) + || in_consteval_if_p); +} + /* Return true if a call to FN with number of arguments NARGS is an immediate invocation. */ @@ -9033,12 +9046,7 @@ immediate_invocation_p (tree fn, int nar { return (TREE_CODE (fn) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (fn) - && cp_unevaluated_operand == 0 - && (current_function_decl == NULL_TREE - || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) - && (current_binding_level->kind != sk_function_parms - || !current_binding_level->immediate_fn_ctx_p) - && !in_consteval_if_p + && !in_immediate_context () /* As an exception, we defer std::source_location::current () invocations until genericization because LWG3396 mandates special behavior for it. */ @@ -9451,6 +9459,12 @@ build_over_call (struct z_candidate *can } /* Default arguments */ + bool save_in_consteval_if_p = in_consteval_if_p; + /* If the call is immediate function invocation, make sure + taking address of immediate functions is allowed in default + arguments. */ + if (immediate_invocation_p (STRIP_TEMPLATE (fn), nargs)) + in_consteval_if_p = true; for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++) { if (TREE_VALUE (parm) == error_mark_node) @@ -9463,6 +9477,7 @@ build_over_call (struct z_candidate *can return error_mark_node; argarray[j++] = val; } + in_consteval_if_p = save_in_consteval_if_p; /* Ellipsis */ int magic = magic_varargs_p (fn); --- gcc/cp/typeck.c.jj 2021-10-18 11:01:08.635858336 +0200 +++ gcc/cp/typeck.c 2021-10-19 10:42:18.454671723 +0200 @@ -6773,9 +6773,19 @@ cp_build_addr_expr_1 (tree arg, bool str return error_mark_node; } + if (TREE_CODE (t) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (t) + && !in_immediate_context ()) + { + if (complain & tf_error) + error_at (loc, "taking address of an immediate function %qD", + t); + return error_mark_node; + } + type = build_ptrmem_type (context_for_name_lookup (t), TREE_TYPE (t)); - t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1)); + t = make_ptrmem_cst (type, t); return t; } @@ -6800,9 +6810,7 @@ cp_build_addr_expr_1 (tree arg, bool str tree stripped_arg = tree_strip_any_location_wrapper (arg); if (TREE_CODE (stripped_arg) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (stripped_arg) - && cp_unevaluated_operand == 0 - && (current_function_decl == NULL_TREE - || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) + && !in_immediate_context ()) { if (complain & tf_error) error_at (loc, "taking address of an immediate function %qD", --- gcc/cp/constexpr.c.jj 2021-10-19 09:24:41.938242276 +0200 +++ gcc/cp/constexpr.c 2021-10-19 12:22:35.583964001 +0200 @@ -7276,6 +7276,10 @@ find_immediate_fndecl (tree *tp, int */* { if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp)) return *tp; + if (TREE_CODE (*tp) == PTRMEM_CST + && TREE_CODE (PTRMEM_CST_MEMBER (*tp)) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (*tp))) + return PTRMEM_CST_MEMBER (*tp); return NULL_TREE; } --- gcc/testsuite/g++.dg/cpp2a/consteval20.C.jj 2021-10-19 10:29:30.471484783 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval20.C 2021-10-19 10:29:30.471484783 +0200 @@ -0,0 +1,24 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + consteval int foo () const { return 42; } +}; + +constexpr S s; + +int +bar () +{ + return (s.*&S::foo) (); // { dg-error "taking address of an immediate function" } +} + +constexpr auto a = &S::foo; // { dg-error "taking address of an immediate function" } + +consteval int +baz () +{ + return (s.*&S::foo) (); +} + +static_assert (baz () == 42); --- gcc/testsuite/g++.dg/cpp2a/consteval21.C.jj 2021-10-19 11:09:18.890838778 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval21.C 2021-10-19 11:57:28.309175141 +0200 @@ -0,0 +1,35 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; + +consteval int foo () { return 42; } + +consteval int +bar (int (*fn) () = &foo) +{ + return fn (); +} + +consteval int +baz (int (S::*fn) () = &S::foo) +{ + S s; + return (s.*fn) (); +} + +consteval int +qux (int (S::*fn) () = &S::bar) +{ + S s; + return (s.*fn) (); +} + +static_assert (bar () == 42); +static_assert (baz () == 1); +static_assert (qux () == 2); --- gcc/testsuite/g++.dg/cpp2a/consteval22.C.jj 2021-10-19 11:21:44.271346868 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval22.C 2021-10-19 12:23:31.783173408 +0200 @@ -0,0 +1,34 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; +typedef int (S::*P) (); + +consteval P +foo () +{ + return &S::foo; +} + +consteval P +bar () +{ + return &S::bar; +} + +consteval int +baz () +{ + S s; + return (s.*(foo ())) () + (s.*(bar ())) (); +} + +static_assert (baz () == 3); + +constexpr P a = foo (); // { dg-error "immediate evaluation returns address of immediate function" } +constexpr P b = bar (); // { dg-error "immediate evaluation returns address of immediate function" } --- gcc/testsuite/g++.dg/cpp2a/consteval23.C.jj 2021-10-19 12:23:54.235857548 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval23.C 2021-10-19 12:24:33.931299123 +0200 @@ -0,0 +1,12 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } + +consteval int foo () { return 42; } + +consteval int +bar (int (*fn) () = foo) +{ + return fn (); +} + +static_assert (bar () == 42); --- gcc/testsuite/g++.dg/cpp23/consteval-if11.C.jj 2021-10-19 11:17:25.964982502 +0200 +++ gcc/testsuite/g++.dg/cpp23/consteval-if11.C 2021-10-19 11:35:38.878602026 +0200 @@ -0,0 +1,27 @@ +// PR c++/102753 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +struct S { + constexpr S () : s (0) {} + consteval int foo () { return 1; } + virtual consteval int bar () { return 2; } + int s; +}; + +consteval int foo () { return 42; } + +constexpr int +bar () +{ + if consteval { // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + int (*fn1) () = foo; + int (S::*fn2) () = &S::foo; + int (S::*fn3) () = &S::bar; + S s; + return fn1 () + (s.*fn2) () + (s.*fn3) (); + } + return 0; +} + +static_assert (bar () == 45);