r13-6098-g46711ff8e60d64 made make_typename_type no longer ignore non-types during the lookup, unless the TYPENAME_TYPE in question was followed by the :: scope resolution operator. But there is another exception to this rule: we need to ignore non-types during the lookup also if the TYPENAME_TYPE was named with a tag other than 'typename', such as 'struct' or 'enum', as per [dcl.type.elab]/5.
This patch implements this additional exception. It occurred to me that the tf_qualifying_scope flag is probably unnecessary if we'd use the scope_type tag more thoroughly, but that requires parser changes that are probably too risky at this stage. (I'm working on addressing the FIXME/TODOs here for GCC 14.) Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? PR c++/109420 gcc/cp/ChangeLog: * decl.cc (make_typename_type): Also ignore non-types during the lookup if tag_type is something other than none_type or typename_type. * pt.cc (tsubst) <case TYPENAME_TYPE>: Pass class_type or enum_type as tag_type to make_typename_type as appropriate instead of always passing typename_type. gcc/testsuite/ChangeLog: * g++.dg/template/typename27.C: New test. --- gcc/cp/decl.cc | 9 ++++++++- gcc/cp/pt.cc | 9 ++++++++- gcc/testsuite/g++.dg/template/typename27.C | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/typename27.C diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 5369714f9b3..a0a20c5accc 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -4307,7 +4307,14 @@ make_typename_type (tree context, tree name, enum tag_types tag_type, lookup will stop when we hit a dependent base. */ if (!dependent_scope_p (context)) { - bool want_type = (complain & tf_qualifying_scope); + /* As per [dcl.type.elab]/5 and [temp.res.general]/3, ignore non-types if + the tag corresponds to a class-key or 'enum' (or is scope_type), or if + this typename is followed by :: as per [basic.lookup.qual.general]/1. + TODO: If we'd set the scope_type tag accurately on all TYPENAME_TYPEs + that are followed by :: then we wouldn't need the tf_qualifying_scope + flag. */ + bool want_type = (tag_type != none_type && tag_type != typename_type) + || (complain & tf_qualifying_scope); t = lookup_member (context, name, /*protect=*/2, want_type, complain); } else diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 821e0035c08..1e778e6e786 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -16580,9 +16580,16 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) return error_mark_node; } + /* FIXME: TYPENAME_IS_CLASS_P conflates 'union' vs 'struct' vs 'class' + tags. TYPENAME_TYPE should probably remember the exact tag that + was used. */ + enum tag_types tag + = TYPENAME_IS_CLASS_P (t) ? class_type + : TYPENAME_IS_ENUM_P (t) ? enum_type + : typename_type; tsubst_flags_t tcomplain = complain | tf_keep_type_decl; tcomplain |= tst_ok_flag | qualifying_scope_flag; - f = make_typename_type (ctx, f, typename_type, tcomplain); + f = make_typename_type (ctx, f, tag, tcomplain); if (f == error_mark_node) return f; if (TREE_CODE (f) == TYPE_DECL) diff --git a/gcc/testsuite/g++.dg/template/typename27.C b/gcc/testsuite/g++.dg/template/typename27.C new file mode 100644 index 00000000000..61b3efd998e --- /dev/null +++ b/gcc/testsuite/g++.dg/template/typename27.C @@ -0,0 +1,19 @@ +// PR c++/109420 + +struct A { + struct X { }; + int X; +}; + +struct B { + enum E { }; + enum F { E }; +}; + +template<class T, class U> +void f() { + struct T::X x; // OK, lookup ignores the data member 'int A::X' + enum U::E e; // OK, lookup ignores the enumerator 'B::F::E' +} + +template void f<A, B>(); -- 2.40.0.153.g6369acd968