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..5fc9e5efdab 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..a115e1d128c 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 + a 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. */ + tree filtered_fns = NULL_TREE; + unsigned filtered_size = 0; + 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); + if (maybe_incomplete) + { + /* 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. */ + 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..8f8e5d9e3d4 --- /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 void f(); // #1 + template static auto g() -> decltype(f()); + template static void f(...); // #2 +}; + +int main() { + A<>::g(); // 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..b0d1bbe985a --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16b.C @@ -0,0 +1,37 @@ +// Like non-dependent16a.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(); +}