C++ PATCH for c++/88325 - ICE with invalid out-of-line template member definition
Jason Merrill
jason@redhat.com
Tue Feb 5 15:50:00 GMT 2019
On Mon, Feb 4, 2019 at 4:52 PM Marek Polacek <polacek@redhat.com> wrote:
> On Mon, Feb 04, 2019 at 09:44:01AM -0500, Jason Merrill wrote:
> > On 2/1/19 2:55 PM, Marek Polacek wrote:
> > > On Fri, Feb 01, 2019 at 12:02:44PM -0500, Jason Merrill wrote:
> > > > On 2/1/19 11:26 AM, Marek Polacek wrote:
> > > > > On Wed, Jan 30, 2019 at 01:39:11PM -0500, Jason Merrill wrote:
> > > > > > On 1/28/19 9:46 PM, Marek Polacek wrote:
> > > > > > > This patch fixes an ICE-on-invalid (becase out-of-line constructors can't have
> > > > > > > template arguments and also because function templates can't be partially
> > > > > > > specialized) in C++2a: when we're parsing
> > > > > > >
> > > > > > > template<typename T> template<typename U> A<T>::A<U> ()
> > > > > > >
> > > > > > > in the attached test we end up parsing "A<T>::A<U>" as a type name, and first we
> > > > > > > try a class-name. First we process "A<T>::" as the nested name specifier and then
> > > > > > > we parse "A<U>". In this test that results in a BASELINK. Because in this context
> > > > > > > we're supposed to treat it as a typename ([temp.res]/6), we call make_typename_type,
> > > > > > > but that crashes.
> > > > > >
> > > > > > Hmm. If we've done an actual lookup (that gave us a BASELINK), we aren't
> > > > > > dealing with a member of an unknown specialization anymore, so we should
> > > > > > just use the result of the lookup rather than speculate about what the name
> > > > > > might mean. Why are we still trying to treat it as a typename?
> > > > >
> > > > > Good point. It's because cp_parser_class_name has:
> > > > >
> > > > > 23095 /* Any name names a type if we're following the `typename' keyword
> > > > > 23096 in a qualified name where the enclosing scope is type-dependent. */
> > > > > 23097 typename_p = (typename_keyword_p && scope && TYPE_P (scope)
> > > > > 23098 && dependent_type_p (scope));
> > > > >
> > > > > and scope is in this case "A<T>" which is dependent. Then there's this
> > > > > "identifier, but not template-id" case which only performs name lookup when
> > > > > typename_p is false. But we're parsing "A<U>" so we call
> > > > > cp_parser_template_id. It sees CPP_TEMPLATE_ID -- an already parsed
> > > > > template-id, so it just returns it, which is a BASELINK. So even messing
> > > > > with tag_type wouldn't help.
> > > > >
> > > > > Does this answer your question?
> > > >
> > > > Mostly, though I'm still curious how we got the previous parse of the
> > > > template-id and yet continued to try to parse it as a class-name.
> > >
> > > So we have
> > > template<typename T> template<typename U>
> > > A<T>::A<U> ()
> > >
> > > and we're in cp_parser_single_declaration. We're trying to parse the
> > > decl-specifier-seq, and that first tries to parse variou RID_* keywords
> > > and if that doesn't work it tries to parse a constructor:
> > >
> > > /* Constructors are a special case. The `S' in `S()' is not a
> > > decl-specifier; it is the beginning of the declarator. */
> > > constructor_p
> > > = (!found_decl_spec
> > > && constructor_possible_p
> > > && (cp_parser_constructor_declarator_p
> > > (parser, decl_spec_seq_has_spec_p (decl_specs, ds_friend))));
> > >
> > > cp_parser_constructor_declarator_p calls cp_parser_nested_name_specifier_opt
> > > and that's where we parse the two template-ids, A<T> and A<U>. But
> > > cp_parser_constructor_declarator_p isn't successful so we go to parsing
> > > A<T>::A<U> as a type-specifier, and that's where the above happens.
> >
> > Aha. That seems like a bug in cp_parser_constructor_declarator.
>
> Sorry, what specifically, that cp_parser_constructor_declarator_p thinks it's
> not a ctor? I thought it was an invalid ctor, since "out-of-line constructor
> for 'A' cannot have template arguments".
True, good point, though it is still more a constructor declarator
than anything else. It would be nice for the diagnostic to suggest
removing the redundant template arguments aren't allowed, not just the
current confusing message about a partial specialization. The
diagnostic for a similar declaration of a nested class template is
"partial specialization ‘struct A<T>::B<U>’ does not specialize any
template arguments; to define the primary template, remove the
template argument list".
We also shouldn't mention the internal name "__ct".
> Once again, we're parsing "A<T>::A<U> ()". "A<T>::" is parsed as a nested-
> name-specifier = struct A. Then:
>
> 27377 /* If we have a class scope, this is easy; DR 147 says that S::S always
> 27378 names the constructor, and no other qualified name could. */
> 27379 if (constructor_p && nested_name_specifier
> 27380 && CLASS_TYPE_P (nested_name_specifier))
> 27381 {
> 27382 tree id = cp_parser_unqualified_id (parser,
> 27383 /*template_keyword_p=*/false,
> 27384 /*check_dependency_p=*/false,
> 27385 /*declarator_p=*/true,
> 27386 /*optional_p=*/false);
> 27387 if (is_overloaded_fn (id))
> 27388 id = DECL_NAME (get_first_fn (id));
> 27389 if (!constructor_name_p (id, nested_name_specifier))
> 27390 constructor_p = false;
> 27391 }
>
> 'id' is a BASELINK, we extract its name which is "__ct". And constructor_name_p
> returns false because __ct != A. What would you like we do here?
__ct is ctor_identifier, which certainly names a constructor, and we
got this BASELINK by looking up the name of the class, so I think
returning true would be more appropriate.
Jason
More information about the Gcc-patches
mailing list