aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2021-01-27 00:51:01 -0500
committerJason Merrill <jason@redhat.com>2021-01-27 15:08:05 -0500
commit9cd7c32549fa334885b716fe98b674f6447fa7c0 (patch)
treea75a9c54c180f5e49cceec4f6ec3e25cb248cb10 /gcc
parent55163419211c6f17e3e22c68304384eba35782a3 (diff)
downloadgcc-9cd7c32549fa334885b716fe98b674f6447fa7c0.zip
gcc-9cd7c32549fa334885b716fe98b674f6447fa7c0.tar.gz
gcc-9cd7c32549fa334885b716fe98b674f6447fa7c0.tar.bz2
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/name-lookup.c144
-rw-r--r--gcc/cp/pt.c41
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/decltype37.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/inh-ctor36.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/inh-ctor39.C12
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/using-enum-7.C27
-rw-r--r--gcc/testsuite/g++.dg/lookup/using4.C2
-rw-r--r--gcc/testsuite/g++.dg/template/crash75.C4
-rw-r--r--gcc/testsuite/g++.dg/template/crash76.C2
9 files changed, 154 insertions, 90 deletions
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 0fb0036..52e4a63 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 %<using enum %T::%D%>?",
- 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 %<using enum %T::%D%>?",
+ 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 63a0a11..8f05ce2 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 c885e9a..5d0f085 100644
--- a/gcc/testsuite/g++.dg/cpp0x/decltype37.C
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype37.C
@@ -8,7 +8,7 @@ template<typename T> auto foo(T* t) -> wrap<T>* { return 0; }
template<typename T>
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<int> 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 0000000..c531af9
--- /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 <class... T> struct C: T...
+{
+ using A::A;
+};
+
+C<A> 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 0000000..3e6356c
--- /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 <class... T> struct C: T...
+{
+ using T::T...;
+};
+
+C<A,B> c1(42);
+C<A,B> 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 0000000..4ba3b9e
--- /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 <typename UQ>
+int
+v4 ()
+{
+ using UQ::kl;
+ return kl;
+}
+
+template <typename UQ>
+int
+v5 ()
+{
+ using UQ::kl; // { dg-error "not a class" }
+ return kl; // { dg-error "not declared" }
+}
+
+int main()
+{
+ v4<A>();
+ v4<A::E>();
+ v5<int>();
+}
diff --git a/gcc/testsuite/g++.dg/lookup/using4.C b/gcc/testsuite/g++.dg/lookup/using4.C
index facb2b4..a9a8ec2 100644
--- a/gcc/testsuite/g++.dg/lookup/using4.C
+++ b/gcc/testsuite/g++.dg/lookup/using4.C
@@ -10,6 +10,6 @@ template <class T>
struct Bar : public Foo<T> {
void foo()
{
- using Foo<T>::i; // { dg-error "member at non-class scope" }
+ using Foo<T>::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 462be95..2bdc395 100644
--- a/gcc/testsuite/g++.dg/template/crash75.C
+++ b/gcc/testsuite/g++.dg/template/crash75.C
@@ -2,7 +2,9 @@
template<typename T> 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<int*> a;
diff --git a/gcc/testsuite/g++.dg/template/crash76.C b/gcc/testsuite/g++.dg/template/crash76.C
index 851cdd8..2711749 100644
--- a/gcc/testsuite/g++.dg/template/crash76.C
+++ b/gcc/testsuite/g++.dg/template/crash76.C
@@ -7,7 +7,7 @@ template<typename> struct A
template<typename T> struct B
{
- using A<T>::X::Y; // { dg-error "not a base type" }
+ using A<T>::X::Y; // { dg-error "not a class" }
};
B<int> b;