c++: improve lookup of member-qualified names

I've been working on the resolution of CWG1835 by P1787, which among many
other things clarified that a name after -> or . is looked up first in the
class of the object expression even if it's dependent.  This patch does not
make that change; this is a smaller change extracted from that work in
progress to make the lookup in the object type work better in cases where
unqualified lookup doesn't find anything.

Basically, if we see "t.foo::" we know that looking up foo in t needs to
find a type, so we build an implicit TYPENAME_TYPE for it.

This also implements the change from P1787 to assume that a name followed by
< in a type-only context names a template, since the less-than operator
can't appear in a type context.  This makes some of the lines in dtor11.C
work.

I introduce the predicate 'dependentish_scope_p' for the case where the
current instantiation has dependent bases, so even though we can perform
name lookup, we can't conclude that a lookup failure is conclusive.

gcc/cp/ChangeLog:

	* cp-tree.h (dependentish_scope_p): Declare.
	* pt.c (dependentish_scope_p): New.
	* parser.c (cp_parser_lookup_name): Return a TYPENAME_TYPE
	for lookup of a type in a dependent object.
	(cp_parser_template_id): Handle TYPENAME_TYPE.
	(cp_parser_template_name): If we're looking for a type,
	a name followed by < names a template.

gcc/testsuite/ChangeLog:

	* g++.dg/template/dtor5.C: Adjust expected error.
	* g++.dg/cpp23/lookup2.C: New test.
	* g++.dg/template/dtor11.C: New test.
This commit is contained in:
Jason Merrill 2021-09-17 14:18:55 -04:00
parent 8618f9e58c
commit 18b57c1d4a
6 changed files with 92 additions and 17 deletions

View File

@ -7263,6 +7263,7 @@ extern tree maybe_get_template_decl_from_type_decl (tree);
extern int processing_template_parmlist;
extern bool dependent_type_p (tree);
extern bool dependent_scope_p (tree);
extern bool dependentish_scope_p (tree);
extern bool any_dependent_template_arguments_p (const_tree);
extern bool any_erroneous_template_args_p (const_tree);
extern bool dependent_template_p (tree);

View File

@ -18187,6 +18187,16 @@ cp_parser_template_id (cp_parser *parser,
if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR)
SET_EXPR_LOCATION (template_id, combined_loc);
}
else if (TREE_CODE (templ) == TYPE_DECL
&& TREE_CODE (TREE_TYPE (templ)) == TYPENAME_TYPE)
{
/* Some type template in dependent scope. */
tree &name = TYPENAME_TYPE_FULLNAME (TREE_TYPE (templ));
name = build_min_nt_loc (combined_loc,
TEMPLATE_ID_EXPR,
name, arguments);
template_id = templ;
}
else
{
/* If it's not a class-template or a template-template, it should be
@ -18413,8 +18423,8 @@ cp_parser_template_name (cp_parser* parser,
}
/* cp_parser_lookup_name clears OBJECT_TYPE. */
const bool scoped_p = ((parser->scope ? parser->scope
: parser->context->object_type) != NULL_TREE);
tree scope = (parser->scope ? parser->scope
: parser->context->object_type);
/* Look up the name. */
decl = cp_parser_lookup_name (parser, identifier,
@ -18427,6 +18437,19 @@ cp_parser_template_name (cp_parser* parser,
decl = strip_using_decl (decl);
/* 13.3 [temp.names] A < is interpreted as the delimiter of a
template-argument-list if it follows a name that is not a
conversion-function-id and
- that follows the keyword template or a ~ after a nested-name-specifier or
in a class member access expression, or
- for which name lookup finds the injected-class-name of a class template
or finds any declaration of a template, or
- that is an unqualified name for which name lookup either finds one or
more functions or finds nothing, or
- that is a terminal name in a using-declarator (9.9), in a declarator-id
(9.3.4), or in a type-only context other than a nested-name-specifier
(13.8). */
/* If DECL is a template, then the name was a template-name. */
if (TREE_CODE (decl) == TEMPLATE_DECL)
{
@ -18454,11 +18477,7 @@ cp_parser_template_name (cp_parser* parser,
}
else
{
/* The standard does not explicitly indicate whether a name that
names a set of overloaded declarations, some of which are
templates, is a template-name. However, such a name should
be a template-name; otherwise, there is no way to form a
template-id for the overloaded templates. */
/* Look through an overload set for any templates. */
bool found = false;
for (lkp_iterator iter (MAYBE_BASELINK_FUNCTIONS (decl));
@ -18466,34 +18485,41 @@ cp_parser_template_name (cp_parser* parser,
if (TREE_CODE (*iter) == TEMPLATE_DECL)
found = true;
/* "an unqualified name for which name lookup either finds one or more
functions or finds nothing". */
if (!found
&& (cxx_dialect > cxx17)
&& !scoped_p
&& !scope
&& cp_lexer_next_token_is (parser->lexer, CPP_LESS)
&& tag_type == none_type)
{
/* [temp.names] says "A name is also considered to refer to a template
if it is an unqualified-id followed by a < and name lookup finds
either one or more functions or finds nothing." */
/* The "more functions" case. Just use the OVERLOAD as normally.
We don't use is_overloaded_fn here to avoid considering
BASELINKs. */
if (TREE_CODE (decl) == OVERLOAD
/* Name lookup found one function. */
|| TREE_CODE (decl) == FUNCTION_DECL)
|| TREE_CODE (decl) == FUNCTION_DECL
/* Name lookup found nothing. */
|| decl == error_mark_node)
found = true;
/* Name lookup found nothing. */
else if (decl == error_mark_node)
return identifier;
}
/* "in a type-only context" */
if (!found && scope
&& tag_type != none_type
&& dependentish_scope_p (scope)
&& cp_parser_nth_token_starts_template_argument_list_p (parser, 1))
found = true;
if (!found)
{
/* The name does not name a template. */
cp_parser_error (parser, "expected template-name");
return error_mark_node;
}
else if (decl == error_mark_node)
/* Repeat the lookup at instantiation time. */
decl = identifier;
}
return decl;
@ -30373,6 +30399,17 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
consider class templates. */
: is_template ? LOOK_want::TYPE
: prefer_type_arg (tag_type));
/* If we know we're looking for a type (e.g. A in p->A::x),
mock up a typename. */
if (!decl && object_type && tag_type != none_type
&& dependentish_scope_p (object_type))
{
tree type = build_typename_type (object_type, name, name,
typename_type);
decl = TYPE_NAME (type);
}
parser->object_scope = object_type;
parser->qualifying_scope = NULL_TREE;
}

View File

@ -26970,6 +26970,15 @@ dependent_scope_p (tree scope)
&& !currently_open_class (scope));
}
/* True if we might find more declarations in SCOPE during instantiation than
we can when parsing the template. */
bool
dependentish_scope_p (tree scope)
{
return dependent_scope_p (scope) || any_dependent_bases_p (scope);
}
/* T is a SCOPE_REF. Return whether it represents a non-static member of
an unknown base of 'this' (and is therefore instantiation-dependent). */

View File

@ -0,0 +1,6 @@
// DR 1835
template <class T> void f(T t) { t.foo::bar(); }
struct foo { void bar(); };
struct baz : foo { };
int main() { f(baz()); }

View File

@ -0,0 +1,22 @@
template <class T>
struct B
{
void f(T *p)
{
p->template A<int>::~A<int>();
p->A::~A();
p->~A<int>();
p->~A();
p->~T();
p->T::~T();
}
};
template <class T>
struct A
{ };
int main()
{
B<A<int> >().f(0);
}

View File

@ -11,7 +11,7 @@ template <class T> void f(A<T> *ap) {
}
template <class T> void g(A<T> *ap) {
ap->~B(); // { dg-error "destructor name" }
ap->~B(); // { dg-error "" }
}
int main()