From 9cd7c32549fa334885b716fe98b674f6447fa7c0 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 27 Jan 2021 00:51:01 -0500 Subject: [PATCH] c++: Dependent using enum [PR97874] The handling of dependent scopes and unsuitable scopes in lookup_using_decl was a bit convoluted; I tweaked it for a while and then eventually reorganized much of the function to hopefully be clearer. Along the way I noticed a couple of ways we were mishandling inherited constructors. The local binding for a dependent using is the USING_DECL. Implement instantiation of a dependent USING_DECL at function scope. gcc/cp/ChangeLog: PR c++/97874 * name-lookup.c (lookup_using_decl): Clean up handling of dependency and inherited constructors. (finish_nonmember_using_decl): Handle DECL_DEPENDENT_P. * pt.c (tsubst_expr): Handle DECL_DEPENDENT_P. gcc/testsuite/ChangeLog: PR c++/97874 * g++.dg/lookup/using4.C: No error in C++20. * g++.dg/cpp0x/decltype37.C: Adjust message. * g++.dg/template/crash75.C: Adjust message. * g++.dg/template/crash76.C: Adjust message. * g++.dg/cpp0x/inh-ctor36.C: New test. * g++.dg/cpp1z/inh-ctor39.C: New test. * g++.dg/cpp2a/using-enum-7.C: New test. --- gcc/cp/name-lookup.c | 144 +++++++++++----------- gcc/cp/pt.c | 41 +++--- gcc/testsuite/g++.dg/cpp0x/decltype37.C | 2 +- gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C | 10 ++ gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C | 12 ++ gcc/testsuite/g++.dg/cpp2a/using-enum-7.C | 27 ++++ gcc/testsuite/g++.dg/lookup/using4.C | 2 +- gcc/testsuite/g++.dg/template/crash75.C | 4 +- gcc/testsuite/g++.dg/template/crash76.C | 2 +- 9 files changed, 154 insertions(+), 90 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/using-enum-7.C diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 0fb0036c4f3..52e4a630e25 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -5729,6 +5729,16 @@ lookup_using_decl (tree scope, name_lookup &lookup) /* Naming a class member. This is awkward in C++20, because we might be naming an enumerator of an unrelated class. */ + tree npscope = scope; + if (PACK_EXPANSION_P (scope)) + npscope = PACK_EXPANSION_PATTERN (scope); + + if (!MAYBE_CLASS_TYPE_P (npscope)) + { + error ("%qT is not a class, namespace, or enumeration", npscope); + return NULL_TREE; + } + /* You cannot using-decl a destructor. */ if (TREE_CODE (lookup.name) == BIT_NOT_EXPR) { @@ -5737,14 +5747,13 @@ lookup_using_decl (tree scope, name_lookup &lookup) } /* Using T::T declares inheriting ctors, even if T is a typedef. */ - if (MAYBE_CLASS_TYPE_P (scope) - && (lookup.name == TYPE_IDENTIFIER (scope) - || constructor_name_p (lookup.name, scope))) + if (lookup.name == TYPE_IDENTIFIER (npscope) + || constructor_name_p (lookup.name, npscope)) { if (!TYPE_P (current)) { error ("non-member using-declaration names constructor of %qT", - scope); + npscope); return NULL_TREE; } maybe_warn_cpp0x (CPP0X_INHERITING_CTORS); @@ -5752,88 +5761,79 @@ lookup_using_decl (tree scope, name_lookup &lookup) CLASSTYPE_NON_AGGREGATE (current) = true; } - if (!MAYBE_CLASS_TYPE_P (scope)) - ; + if (!TYPE_P (current) && cxx_dialect < cxx20) + { + error ("using-declaration for member at non-class scope"); + return NULL_TREE; + } + + bool depscope = dependent_scope_p (scope); + + if (depscope) + /* Leave binfo null. */; else if (TYPE_P (current)) { - dependent_p = dependent_scope_p (scope); - if (!dependent_p) + binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none); + gcc_checking_assert (b_kind >= bk_not_base); + + if (b_kind == bk_not_base && any_dependent_bases_p ()) + /* Treat as-if dependent. */ + depscope = true; + else if (lookup.name == ctor_identifier + && (b_kind < bk_proper_base || !binfo_direct_p (binfo))) { - binfo = lookup_base (current, scope, ba_any, &b_kind, tf_none); - gcc_checking_assert (b_kind >= bk_not_base); - - if (lookup.name == ctor_identifier) + if (any_dependent_bases_p ()) + depscope = true; + else { - /* Even if there are dependent bases, SCOPE will not - be direct base, no matter. */ - if (b_kind < bk_proper_base || !binfo_direct_p (binfo)) - { - error ("%qT is not a direct base of %qT", scope, current); - return NULL_TREE; - } + error ("%qT is not a direct base of %qT", scope, current); + return NULL_TREE; } - else if (b_kind < bk_proper_base) - binfo = TYPE_BINFO (scope); - else if (IDENTIFIER_CONV_OP_P (lookup.name) - && dependent_type_p (TREE_TYPE (lookup.name))) - dependent_p = true; } + + if (b_kind < bk_proper_base) + binfo = TYPE_BINFO (scope); } else binfo = TYPE_BINFO (scope); + dependent_p = (depscope + || (IDENTIFIER_CONV_OP_P (lookup.name) + && dependent_type_p (TREE_TYPE (lookup.name)))); + if (!dependent_p) - { - if (binfo) - lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2, - /*want_type=*/false, tf_none); + lookup.value = lookup_member (binfo, lookup.name, /*protect=*/2, + /*want_type=*/false, tf_none); - tree saved_value = lookup.value; - if (lookup.value - && b_kind < bk_proper_base) + if (!depscope && b_kind < bk_proper_base) + { + if (cxx_dialect >= cxx20 && lookup.value + && TREE_CODE (lookup.value) == CONST_DECL) { - if (cxx_dialect >= cxx20 - && TREE_CODE (lookup.value) == CONST_DECL) - { - /* Using an unrelated enum; check access here rather - than separately for class and non-class using. */ - perform_or_defer_access_check - (binfo, lookup.value, lookup.value, tf_warning_or_error); - /* And then if this is a copy from handle_using_decl, look - through to the original enumerator. */ - if (CONST_DECL_USING_P (lookup.value)) - lookup.value = DECL_ABSTRACT_ORIGIN (lookup.value); - } - else - lookup.value = NULL_TREE; + /* Using an unrelated enum; check access here rather + than separately for class and non-class using. */ + perform_or_defer_access_check + (binfo, lookup.value, lookup.value, tf_warning_or_error); + /* And then if this is a copy from handle_using_decl, look + through to the original enumerator. */ + if (CONST_DECL_USING_P (lookup.value)) + lookup.value = DECL_ABSTRACT_ORIGIN (lookup.value); } - - if (!lookup.value) + else if (!TYPE_P (current)) { - if (!TYPE_P (current)) - { - error ("using-declaration for member at non-class scope"); - return NULL_TREE; - } - - if (b_kind < bk_proper_base) - { - if (b_kind == bk_not_base && any_dependent_bases_p ()) - /* Treat as-if dependent. */ - dependent_p = true; - else - { - auto_diagnostic_group g; - error_not_base_type (scope, current); - if (saved_value && DECL_IMPLICIT_TYPEDEF_P (saved_value) - && (TREE_CODE (TREE_TYPE (saved_value)) - == ENUMERAL_TYPE)) - inform (input_location, - "did you mean %?", - scope, lookup.name); - return NULL_TREE; - } - } + error ("using-declaration for member at non-class scope"); + return NULL_TREE; + } + else + { + auto_diagnostic_group g; + error_not_base_type (scope, current); + if (lookup.value && DECL_IMPLICIT_TYPEDEF_P (lookup.value) + && TREE_CODE (TREE_TYPE (lookup.value)) == ENUMERAL_TYPE) + inform (input_location, + "did you mean %?", + scope, lookup.name); + return NULL_TREE; } } } @@ -6455,6 +6455,8 @@ finish_nonmember_using_decl (tree scope, tree name) else { add_decl_expr (using_decl); + if (DECL_DEPENDENT_P (using_decl)) + lookup.value = using_decl; push_using_decl_bindings (&lookup, name, NULL_TREE); } } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 63a0a110440..8f05ce28899 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -18130,22 +18130,33 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, finish_label_decl (DECL_NAME (decl)); else if (TREE_CODE (decl) == USING_DECL) { - /* We cannot have a member-using decl here (until 'using - enum T' is a thing). */ - gcc_checking_assert (!DECL_DEPENDENT_P (decl)); - - /* This must be a non-dependent using-decl, and we'll have - used the names it found during template parsing. We do - not want to do the lookup again, because we might not - find the things we found then. (Again, using enum T - might mean we have to do things here.) */ tree scope = USING_DECL_SCOPE (decl); - gcc_checking_assert (scope - == tsubst (scope, args, complain, in_decl)); - /* We still need to push the bindings so that we can look up - this name later. */ - push_using_decl_bindings (DECL_NAME (decl), - USING_DECL_DECLS (decl)); + if (DECL_DEPENDENT_P (decl)) + { + scope = tsubst (scope, args, complain, in_decl); + if (!MAYBE_CLASS_TYPE_P (scope) + && TREE_CODE (scope) != ENUMERAL_TYPE) + { + if (complain & tf_error) + error_at (DECL_SOURCE_LOCATION (decl), "%qT is not a " + "class, namespace, or enumeration", scope); + return error_mark_node; + } + finish_nonmember_using_decl (scope, DECL_NAME (decl)); + } + else + { + /* This is a non-dependent using-decl, and we'll have + used the names it found during template parsing. We do + not want to do the lookup again, because we might not + find the things we found then. */ + gcc_checking_assert (scope == tsubst (scope, args, + complain, in_decl)); + /* We still need to push the bindings so that we can look up + this name later. */ + push_using_decl_bindings (DECL_NAME (decl), + USING_DECL_DECLS (decl)); + } } else if (is_capture_proxy (decl) && !DECL_TEMPLATE_INSTANTIATION (current_function_decl)) diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype37.C b/gcc/testsuite/g++.dg/cpp0x/decltype37.C index c885e9a0769..5d0f085f433 100644 --- a/gcc/testsuite/g++.dg/cpp0x/decltype37.C +++ b/gcc/testsuite/g++.dg/cpp0x/decltype37.C @@ -8,7 +8,7 @@ template auto foo(T* t) -> wrap* { return 0; } template struct holder : decltype(*foo((T*)0)) // { dg-error "class type" } { - using decltype(*foo((T*)0))::bar; // { dg-error "is not a base" } + using decltype(*foo((T*)0))::bar; // { dg-error "is not a class" } }; holder h; diff --git a/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C b/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C new file mode 100644 index 00000000000..c531af9bd27 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++11 } } + +struct A { A(); A(int); }; + +template struct C: T... +{ + using A::A; +}; + +C c1(42); diff --git a/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C b/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C new file mode 100644 index 00000000000..3e6356c9935 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++17 } } + +struct A { A(); A(int); }; +struct B { B(); B(void*); }; + +template struct C: T... +{ + using T::T...; +}; + +C c1(42); +C c2(nullptr); diff --git a/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C b/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C new file mode 100644 index 00000000000..4ba3b9ea893 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/using-enum-7.C @@ -0,0 +1,27 @@ +// PR c++/97874 +// { dg-do compile { target c++20 } } + +struct A { enum E { kl }; }; + +template +int +v4 () +{ + using UQ::kl; + return kl; +} + +template +int +v5 () +{ + using UQ::kl; // { dg-error "not a class" } + return kl; // { dg-error "not declared" } +} + +int main() +{ + v4(); + v4(); + v5(); +} diff --git a/gcc/testsuite/g++.dg/lookup/using4.C b/gcc/testsuite/g++.dg/lookup/using4.C index facb2b4fd31..a9a8ec21c4d 100644 --- a/gcc/testsuite/g++.dg/lookup/using4.C +++ b/gcc/testsuite/g++.dg/lookup/using4.C @@ -10,6 +10,6 @@ template struct Bar : public Foo { void foo() { - using Foo::i; // { dg-error "member at non-class scope" } + using Foo::i; // { dg-error "member at non-class scope" "" { target c++17_down } } } }; diff --git a/gcc/testsuite/g++.dg/template/crash75.C b/gcc/testsuite/g++.dg/template/crash75.C index 462be95b2f7..2bdc3950f54 100644 --- a/gcc/testsuite/g++.dg/template/crash75.C +++ b/gcc/testsuite/g++.dg/template/crash75.C @@ -2,7 +2,9 @@ template struct A { - T::X<0> x; // { dg-error "non-template|T::template|base type" } + T::X<0> x; // { dg-error "non-template" } + // { dg-message "T::template" "" { target *-*-* } .-1 } + // { dg-prune-output "is not a class" } }; A a; diff --git a/gcc/testsuite/g++.dg/template/crash76.C b/gcc/testsuite/g++.dg/template/crash76.C index 851cdd8c436..2711749eca9 100644 --- a/gcc/testsuite/g++.dg/template/crash76.C +++ b/gcc/testsuite/g++.dg/template/crash76.C @@ -7,7 +7,7 @@ template struct A template struct B { - using A::X::Y; // { dg-error "not a base type" } + using A::X::Y; // { dg-error "not a class" } }; B b; -- 2.30.2