From patchwork Mon Dec 20 17:46:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrick Palka X-Patchwork-Id: 49120 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 B5DFA3858430 for ; Mon, 20 Dec 2021 17:47:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B5DFA3858430 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1640022450; bh=1tRXF1q+L9t1QL7pR7Qwu0KerHhuDCuUw+XGexOZ9WE=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=Rf0z49SuDAo03hS7QktJWrNDfl5OsrV8tgXsxeseGq3qudOfK4ynnRLY0bamueceW bvI19uAtV5+ESRE8HTiZjSWuDZOFN/G3gNjXy3c/CPNVO41ml9bgv+4rot1A+s7/SB xRQuKa0mmzWbQQkELHjgoBZOkPBoCNr4dvfVoJn8= 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.129.124]) by sourceware.org (Postfix) with ESMTPS id C79083858C60 for ; Mon, 20 Dec 2021 17:47:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C79083858C60 Received: from mail-qv1-f69.google.com (mail-qv1-f69.google.com [209.85.219.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-577-wYti_6-fM2Sk5ex1STFphg-1; Mon, 20 Dec 2021 12:46:59 -0500 X-MC-Unique: wYti_6-fM2Sk5ex1STFphg-1 Received: by mail-qv1-f69.google.com with SMTP id kk20-20020a056214509400b004110a8ba7beso7525828qvb.20 for ; Mon, 20 Dec 2021 09:46:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=1tRXF1q+L9t1QL7pR7Qwu0KerHhuDCuUw+XGexOZ9WE=; b=kDQGizBWX4jMHBa9TpsXmVjSx5KWSZqEiEmmIFKajOldbleExFpuJ5be1Tw29qMmEE Zd8wIZAsrWd6S+iveEe78imOsCGQQWQm8dulPzNf09v7E0AbGgSr0PWTdw0Pg2lnhRKd XydBaQt2M2UugmbilsuHjDbrNVEXT6PdiUy1yluqHRPSqWiw3eaL9Zl94wDof+6l+3BL oodituJWBExyJKlHneVEjZxClIUnL2v3Gxed9maV8OObI8LdGs5RMQyFmf9Db0Zr8gPi HIgkrOa1e1yJF9RGaayCXfa8VxSEeeoqw+m1y+x+d6m1BPvtJ+T1VN6RxEZBWtfUSf9y FpbA== X-Gm-Message-State: AOAM532+sxU2x+b7TRxdntmQaJYIZpway1b9RFJqH5FIhE1yZk298HcM i3yafFIHHU4L8h5kNizgca+uYViANkLRbmqWY4Re8wI8IjRtNbDpuOWwX25AvOu/YWxn5sY+VhR krVg8EP/i4S+01GQpTE+Mvku30//Gl/2LVe/1mZCQCkd8rbjGteWej1u0uJQpKmqoH8k= X-Received: by 2002:a05:6214:622:: with SMTP id a2mr14267143qvx.109.1640022418416; Mon, 20 Dec 2021 09:46:58 -0800 (PST) X-Google-Smtp-Source: ABdhPJxQJFqRLKOXo0UxWxv0vkave+ow1iiky3PEYUDy1s/m+uROFWOTi5tN0I18KdL3BXSlKe1kMg== X-Received: by 2002:a05:6214:622:: with SMTP id a2mr14267117qvx.109.1640022418005; Mon, 20 Dec 2021 09:46:58 -0800 (PST) Received: from localhost.localdomain (ool-18e40894.dyn.optonline.net. [24.228.8.148]) by smtp.gmail.com with ESMTPSA id 196sm11382885qkd.61.2021.12.20.09.46.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Dec 2021 09:46:57 -0800 (PST) To: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: memfn lookup consistency in incomplete class context Date: Mon, 20 Dec 2021 12:46:56 -0500 Message-Id: <20211220174656.1627621-1-ppalka@redhat.com> X-Mailer: git-send-email 2.34.1.297.g69a9c10c95 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-14.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, 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: Patrick Palka via Gcc-patches From: Patrick Palka Reply-To: Patrick Palka Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" When instantiating a call to a member function of a class template, we repeat the member function lookup in order to obtain the corresponding partially instantiated functions. Within an incomplete-class context however, we need to be more careful when repeating the lookup because we don't want to introduce later-declared member functions that weren't visible at template definition time. We're currently not careful enough in this respect, which causes us to reject memfn1.C below. This patch fixes this issue by making tsubst_baselink filter out from the instantiation-time lookup those member functions that were invisible at template definition time. This is really only necessary within an incomplete-class context, so this patch adds a heuristic flag to BASELINK to help us avoid needlessly performing this filtering step (which would be a no-op) in complete-class contexts. This is also necessary in order for the ahead-of-time pruning implemented in r12-6075 to be effective for member functions within class templates. Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? gcc/cp/ChangeLog: * call.c (build_new_method_call): Set BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P on the pruned baselink. * cp-tree.h (BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P): Define. * pt.c (filter_memfn_lookup): New subroutine of tsubst_baselink. (tsubst_baselink): Use filter_memfn_lookup on the new lookup result when BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P is set on the old baselink. Remove redundant BASELINK_P check. * search.c (build_baselink): Set BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P appropriately. gcc/testsuite/ChangeLog: * g++.dg/lookup/memfn1.C: New test. * g++.dg/template/non-dependent16b.C: New test. --- gcc/cp/call.c | 1 + gcc/cp/cp-tree.h | 5 + gcc/cp/pt.c | 91 ++++++++++++++++++- gcc/cp/search.c | 4 + gcc/testsuite/g++.dg/lookup/memfn1.C | 16 ++++ .../g++.dg/template/non-dependent16b.C | 37 ++++++++ 6 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/lookup/memfn1.C create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16b.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 1fbfc580a1e..bee367f57d7 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -11187,6 +11187,7 @@ build_new_method_call (tree instance, tree fns, vec **args, } orig_fns = copy_node (orig_fns); BASELINK_FUNCTIONS (orig_fns) = fn; + BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (orig_fns) = true; } skip_prune: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8b5cfa230ba..4f175f491cb 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -464,6 +464,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; PACK_EXPANSION_SIZEOF_P (in *_PACK_EXPANSION) OVL_USING_P (in OVERLOAD) IMPLICIT_CONV_EXPR_NONTYPE_ARG (in IMPLICIT_CONV_EXPR) + BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (in BASELINK) 2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -1060,6 +1061,10 @@ struct GTY(()) tree_template_decl { /* Nonzero if this baselink was from a qualified lookup. */ #define BASELINK_QUALIFIED_P(NODE) \ TREE_LANG_FLAG_0 (BASELINK_CHECK (NODE)) +/* Nonzero if the overload set for this baselink might be incomplete due + to the lookup being performed from an incomplete class context. */ +#define BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P(NODE) \ + TREE_LANG_FLAG_1 (BASELINK_CHECK (NODE)) struct GTY(()) tree_baselink { struct tree_common common; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 4f0ae6d5851..7293f4e7b12 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -16221,6 +16221,81 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) } } +/* OLDFNS is a lookup set of member functions from some class template, and + NEWFNS is a lookup set of member functions from a specialization of that + class template. Return the subset of NEWFNS which are specializations of + some function from OLDFNS. */ + +static tree +filter_memfn_lookup (tree oldfns, tree newfns) +{ + /* Record all member functions from the old lookup set OLDFNS into + VISIBLE_SET. */ + hash_set visible_set; + for (tree fn : lkp_range (oldfns)) + { + if (TREE_CODE (fn) == USING_DECL) + { + /* FIXME: Punt on (dependent) USING_DECL for now; mapping + a dependent USING_DECL to its instantiation seems + tricky. */ + gcc_checking_assert (DECL_DEPENDENT_P (fn)); + return newfns; + } + else if (TREE_CODE (fn) == TEMPLATE_DECL) + /* A member function template. */ + visible_set.add (fn); + else if (TREE_CODE (fn) == FUNCTION_DECL) + { + if (DECL_TEMPLATE_INFO (fn)) + /* A non-template member function. */ + visible_set.add (DECL_TI_TEMPLATE (fn)); + else + /* A non-template member function from a non-template base, + injected via a using-decl. */ + visible_set.add (fn); + } + else + gcc_unreachable (); + } + + /* Returns true iff (a less specialized version of) FN appeared in + the old lookup set OLDFNS. */ + auto visible_p = [&visible_set] (tree fn) { + if (TREE_CODE (fn) == FUNCTION_DECL + && !DECL_TEMPLATE_INFO (fn)) + return visible_set.contains (fn); + else if (DECL_TEMPLATE_INFO (fn)) + return visible_set.contains (DECL_TI_TEMPLATE (fn)); + else + gcc_unreachable (); + }; + + bool lookup_changed_p = false; + for (tree fn : lkp_range (newfns)) + if (!visible_p (fn)) + { + lookup_changed_p = true; + break; + } + if (!lookup_changed_p) + return newfns; + + /* Filter out from NEWFNS the member functions that weren't + previously visible according to OLDFNS. */ + unsigned filtered_size = 0; + tree filtered_fns = NULL_TREE; + for (tree fn : lkp_range (newfns)) + if (visible_p (fn)) + { + filtered_fns = lookup_add (fn, filtered_fns); + ++filtered_size; + } + gcc_checking_assert (filtered_size == visible_set.elements ()); + + return filtered_fns; +} + /* tsubst a BASELINK. OBJECT_TYPE, if non-NULL, is the type of the expression on the left-hand side of the "." or "->" operator. We only do the lookup if we had a dependent BASELINK. Otherwise we @@ -16274,8 +16349,21 @@ tsubst_baselink (tree baselink, tree object_type, /* Treat as-if non-dependent below. */ dependent_p = false; + bool maybe_incomplete = BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (baselink); baselink = lookup_fnfields (qualifying_scope, name, /*protect=*/1, complain); + /* Filter out from the new lookup set those functions which didn't + appear in the original lookup set (in a less specialized form). + This is needed to preserve the consistency of member lookup performed + in an incomplete-class context, within which later-declared members + ought to remain invisible. */ + if (maybe_incomplete) + { + BASELINK_FUNCTIONS (baselink) + = filter_memfn_lookup (fns, BASELINK_FUNCTIONS (baselink)); + BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (baselink) = true; + } + if (!baselink) { if ((complain & tf_error) @@ -16285,8 +16373,7 @@ tsubst_baselink (tree baselink, tree object_type, return error_mark_node; } - if (BASELINK_P (baselink)) - fns = BASELINK_FUNCTIONS (baselink); + fns = BASELINK_FUNCTIONS (baselink); } else { diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 943671acff8..b673db960e6 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1091,6 +1091,10 @@ build_baselink (tree binfo, tree access_binfo, tree functions, tree optype) BASELINK_FUNCTIONS (baselink) = functions; BASELINK_OPTYPE (baselink) = optype; + if (binfo == access_binfo + && TYPE_BEING_DEFINED (BINFO_TYPE (access_binfo))) + BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (baselink) = true; + return baselink; } diff --git a/gcc/testsuite/g++.dg/lookup/memfn1.C b/gcc/testsuite/g++.dg/lookup/memfn1.C new file mode 100644 index 00000000000..1bf00119018 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/memfn1.C @@ -0,0 +1,16 @@ +// Verify we preserve the consistency of member function lookup outside of a +// complete-class context. +// { dg-do compile { target c++11 } } + +template +struct A { + template static int f(); // #1 + template static void g(decltype(f())); + template static int f(...); // #2 +}; + +int main() { + A<>::g(0); // OK, the later-declared #2 isn't considered when + // instantiating f(), which would have otherwise + // led to ambiguity. +} diff --git a/gcc/testsuite/g++.dg/template/non-dependent16b.C b/gcc/testsuite/g++.dg/template/non-dependent16b.C new file mode 100644 index 00000000000..eadaeb7f478 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16b.C @@ -0,0 +1,37 @@ +// Like non-dependent16.C, but where A is a template. + +// { dg-do compile { target c++11 } } + +template +struct A { + template static void f(); + template static auto f() -> decltype(f(), 1, *T()); + template static auto f() -> decltype(f(), 2, *T()); + template static auto f() -> decltype(f(), 3, *T()); + template static auto f() -> decltype(f(), 4, *T()); + template static auto f() -> decltype(f(), 5, *T()); + template static auto f() -> decltype(f(), 6, *T()); + template static auto f() -> decltype(f(), 7, *T()); + template static auto f() -> decltype(f(), 8, *T()); + template static auto f() -> decltype(f(), 9, *T()); + template static auto f() -> decltype(f(), 10, *T()); + template static auto f() -> decltype(f(), 11, *T()); + template static auto f() -> decltype(f(), 12, *T()); + template static auto f() -> decltype(f(), 13, *T()); + template static auto f() -> decltype(f(), 14, *T()); + template static auto f() -> decltype(f(), 15, *T()); + template static auto f() -> decltype(f(), 16, *T()); + template static auto f() -> decltype(f(), 17, *T()); + template static auto f() -> decltype(f(), 18, *T()); + template static auto f() -> decltype(f(), 19, *T()); + template static auto f() -> decltype(f(), 20, *T()); + template static auto f() -> decltype(f(), 21, *T()); + template static auto f() -> decltype(f(), 22, *T()); + template static auto f() -> decltype(f(), 23, *T()); + template static auto f() -> decltype(f(), 24, *T()); + template static auto f() -> decltype(f(), 25, *T()); +}; + +int main() { + A<>::f(); +}