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:
parent
8618f9e58c
commit
18b57c1d4a
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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). */
|
||||
|
||||
|
6
gcc/testsuite/g++.dg/cpp23/lookup2.C
Normal file
6
gcc/testsuite/g++.dg/cpp23/lookup2.C
Normal 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()); }
|
22
gcc/testsuite/g++.dg/template/dtor11.C
Normal file
22
gcc/testsuite/g++.dg/template/dtor11.C
Normal 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);
|
||||
}
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user