avoid -Wredundant-tags on a first declaration in use (PR 93824)

Message ID f726a433-0bb9-33d4-9929-7c018913d0ca@gmail.com
State New
Headers show
Series
  • avoid -Wredundant-tags on a first declaration in use (PR 93824)
Related show

Commit Message

Martin Sebor Feb. 24, 2020, 11:58 p.m.
-Wredundant-tags doesn't consider type declarations that are also
the first uses of the type, such as in 'void f (struct S);' and
issues false positives for those.  According to the reported that's
making it harder to use the warning to clean up LibreOffice.

The attached patch extends -Wredundant-tags to avoid these false
positives by relying on the same class_decl_loc_t::class2loc mapping
as -Wmismatched-tags.  The patch also somewhat improves the detection
of both issues in template declarations (though more work is still
needed there).

Tested on x86_64-linux.

Martin

Comments

Jason Merrill Feb. 28, 2020, 4:58 p.m. | #1
On 2/24/20 6:58 PM, Martin Sebor wrote:
> -Wredundant-tags doesn't consider type declarations that are also

> the first uses of the type, such as in 'void f (struct S);' and

> issues false positives for those.  According to the reported that's

> making it harder to use the warning to clean up LibreOffice.

> 

> The attached patch extends -Wredundant-tags to avoid these false

> positives by relying on the same class_decl_loc_t::class2loc mapping

> as -Wmismatched-tags.  The patch also somewhat improves the detection

> of both issues in template declarations (though more work is still

> needed there).


> +	     a new entry for it and return unless it's a declaration

> +	     involving a template that may need to be diagnosed by

> +	     -Wredundant-tags.  */

>  	  *rdl = class_decl_loc_t (class_key, false, def_p);

> -	  return;

> +	  if (TREE_CODE (decl) != TEMPLATE_DECL)

> +	    return;


How can the first appearance of a class template be redundant?

Jason
Martin Sebor Feb. 28, 2020, 5:45 p.m. | #2
On 2/28/20 9:58 AM, Jason Merrill wrote:
> On 2/24/20 6:58 PM, Martin Sebor wrote:

>> -Wredundant-tags doesn't consider type declarations that are also

>> the first uses of the type, such as in 'void f (struct S);' and

>> issues false positives for those.  According to the reported that's

>> making it harder to use the warning to clean up LibreOffice.

>>

>> The attached patch extends -Wredundant-tags to avoid these false

>> positives by relying on the same class_decl_loc_t::class2loc mapping

>> as -Wmismatched-tags.  The patch also somewhat improves the detection

>> of both issues in template declarations (though more work is still

>> needed there).

> 

>> +         a new entry for it and return unless it's a declaration

>> +         involving a template that may need to be diagnosed by

>> +         -Wredundant-tags.  */

>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>> -      return;

>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>> +        return;

> 

> How can the first appearance of a class template be redundant?


I'm not sure I correctly understand the question.  The comment says
"involving a template" (i.e., not one of the first declaration of
a template).  The test case that corresponds to this test is:

   template <class> struct S7 { };
   struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

where DECL is the TEPLATE_DECL of S7<void>.

As I mentioned, more work is still needed to handle templates right
because some redundant tags are still not diagnosed.  For example:

   template <class> struct S7 { };
   template <class T>
   using U = struct S7<T>;   // missing warning

Martin
Jason Merrill Feb. 28, 2020, 8:24 p.m. | #3
On 2/28/20 12:45 PM, Martin Sebor wrote:
> On 2/28/20 9:58 AM, Jason Merrill wrote:

>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>> -Wredundant-tags doesn't consider type declarations that are also

>>> the first uses of the type, such as in 'void f (struct S);' and

>>> issues false positives for those.  According to the reported that's

>>> making it harder to use the warning to clean up LibreOffice.

>>>

>>> The attached patch extends -Wredundant-tags to avoid these false

>>> positives by relying on the same class_decl_loc_t::class2loc mapping

>>> as -Wmismatched-tags.  The patch also somewhat improves the detection

>>> of both issues in template declarations (though more work is still

>>> needed there).

>>

>>> +         a new entry for it and return unless it's a declaration

>>> +         involving a template that may need to be diagnosed by

>>> +         -Wredundant-tags.  */

>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>> -      return;

>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>> +        return;

>>

>> How can the first appearance of a class template be redundant?

> 

> I'm not sure I correctly understand the question.  The comment says

> "involving a template" (i.e., not one of the first declaration of

> a template).  The test case that corresponds to this test is:

> 

>    template <class> struct S7 { };

>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

> 

> where DECL is the TEPLATE_DECL of S7<void>.

> 

> As I mentioned, more work is still needed to handle templates right

> because some redundant tags are still not diagnosed.  For example:

> 

>    template <class> struct S7 { };

>    template <class T>

>    using U = struct S7<T>;   // missing warning


When we get here for an instance of a template, it doesn't make sense to 
treat it as a new type.

If decl is a template and type_decl is an instance of that template, do 
we want to (before the lookup) change type_decl to the template or the 
corresponding generic TYPE_DECL, which should already be in the table?

Jason
Martin Sebor March 9, 2020, 4:31 p.m. | #4
On 2/28/20 1:24 PM, Jason Merrill wrote:
> On 2/28/20 12:45 PM, Martin Sebor wrote:

>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>> issues false positives for those.  According to the reported that's

>>>> making it harder to use the warning to clean up LibreOffice.

>>>>

>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>> positives by relying on the same class_decl_loc_t::class2loc mapping

>>>> as -Wmismatched-tags.  The patch also somewhat improves the detection

>>>> of both issues in template declarations (though more work is still

>>>> needed there).

>>>

>>>> +         a new entry for it and return unless it's a declaration

>>>> +         involving a template that may need to be diagnosed by

>>>> +         -Wredundant-tags.  */

>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>> -      return;

>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>> +        return;

>>>

>>> How can the first appearance of a class template be redundant?

>>

>> I'm not sure I correctly understand the question.  The comment says

>> "involving a template" (i.e., not one of the first declaration of

>> a template).  The test case that corresponds to this test is:

>>

>>    template <class> struct S7 { };

>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>

>> where DECL is the TEPLATE_DECL of S7<void>.

>>

>> As I mentioned, more work is still needed to handle templates right

>> because some redundant tags are still not diagnosed.  For example:

>>

>>    template <class> struct S7 { };

>>    template <class T>

>>    using U = struct S7<T>;   // missing warning

> 

> When we get here for an instance of a template, it doesn't make sense to 

> treat it as a new type.

> 

> If decl is a template and type_decl is an instance of that template, do 

> we want to (before the lookup) change type_decl to the template or the 

> corresponding generic TYPE_DECL, which should already be in the table?


I'm struggling with how to do this.  Given type (a RECORD_TYPE) and
type_decl (a TEMPLATE_DECL) representing the use of a template, how
do I get the corresponding template (or its explicit or partial
specialization) in the three cases below?

   1) Instance of the primary:
      template <class> class A;
      struct A<int> a;

   2) Instance of an explicit specialization:
      template <class> class B;
      template <> struct B<int>;
      class B<int> b;

   3) Instance of a partial specialization:
      template <class> class C;
      template <class T> struct C<T*>;
      class C<int*> c;

By trial and (lots of) error I figured out that in both (1) and (2),
but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns
the template's type_decl.

Is there some function to call to get it in (3), or even better,
in all three cases?

Martin
Jason Merrill March 9, 2020, 7:40 p.m. | #5
On 3/9/20 12:31 PM, Martin Sebor wrote:
> On 2/28/20 1:24 PM, Jason Merrill wrote:

>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>> issues false positives for those.  According to the reported that's

>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>

>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>> positives by relying on the same class_decl_loc_t::class2loc mapping

>>>>> as -Wmismatched-tags.  The patch also somewhat improves the detection

>>>>> of both issues in template declarations (though more work is still

>>>>> needed there).

>>>>

>>>>> +         a new entry for it and return unless it's a declaration

>>>>> +         involving a template that may need to be diagnosed by

>>>>> +         -Wredundant-tags.  */

>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>> -      return;

>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>> +        return;

>>>>

>>>> How can the first appearance of a class template be redundant?

>>>

>>> I'm not sure I correctly understand the question.  The comment says

>>> "involving a template" (i.e., not one of the first declaration of

>>> a template).  The test case that corresponds to this test is:

>>>

>>>    template <class> struct S7 { };

>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>

>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>

>>> As I mentioned, more work is still needed to handle templates right

>>> because some redundant tags are still not diagnosed.  For example:

>>>

>>>    template <class> struct S7 { };

>>>    template <class T>

>>>    using U = struct S7<T>;   // missing warning

>>

>> When we get here for an instance of a template, it doesn't make sense 

>> to treat it as a new type.

>>

>> If decl is a template and type_decl is an instance of that template, 

>> do we want to (before the lookup) change type_decl to the template or 

>> the corresponding generic TYPE_DECL, which should already be in the 

>> table?

> 

> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

> type_decl (a TEMPLATE_DECL) representing the use of a template, how

> do I get the corresponding template (or its explicit or partial

> specialization) in the three cases below?

> 

>    1) Instance of the primary:

>       template <class> class A;

>       struct A<int> a;

> 

>    2) Instance of an explicit specialization:

>       template <class> class B;

>       template <> struct B<int>;

>       class B<int> b;

> 

>    3) Instance of a partial specialization:

>       template <class> class C;

>       template <class T> struct C<T*>;

>       class C<int*> c;

> 

> By trial and (lots of) error I figured out that in both (1) and (2),

> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

> the template's type_decl.

> 

> Is there some function to call to get it in (3), or even better,

> in all three cases?


I think you're looking for most_general_template.

Jason
Martin Sebor March 9, 2020, 9:39 p.m. | #6
On 3/9/20 1:40 PM, Jason Merrill wrote:
> On 3/9/20 12:31 PM, Martin Sebor wrote:

>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>> issues false positives for those.  According to the reported that's

>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>

>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>> positives by relying on the same class_decl_loc_t::class2loc mapping

>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the detection

>>>>>> of both issues in template declarations (though more work is still

>>>>>> needed there).

>>>>>

>>>>>> +         a new entry for it and return unless it's a declaration

>>>>>> +         involving a template that may need to be diagnosed by

>>>>>> +         -Wredundant-tags.  */

>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>> -      return;

>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>> +        return;

>>>>>

>>>>> How can the first appearance of a class template be redundant?

>>>>

>>>> I'm not sure I correctly understand the question.  The comment says

>>>> "involving a template" (i.e., not one of the first declaration of

>>>> a template).  The test case that corresponds to this test is:

>>>>

>>>>    template <class> struct S7 { };

>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>

>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>

>>>> As I mentioned, more work is still needed to handle templates right

>>>> because some redundant tags are still not diagnosed.  For example:

>>>>

>>>>    template <class> struct S7 { };

>>>>    template <class T>

>>>>    using U = struct S7<T>;   // missing warning

>>>

>>> When we get here for an instance of a template, it doesn't make sense 

>>> to treat it as a new type.

>>>

>>> If decl is a template and type_decl is an instance of that template, 

>>> do we want to (before the lookup) change type_decl to the template or 

>>> the corresponding generic TYPE_DECL, which should already be in the 

>>> table?

>>

>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>> do I get the corresponding template (or its explicit or partial

>> specialization) in the three cases below?

>>

>>    1) Instance of the primary:

>>       template <class> class A;

>>       struct A<int> a;

>>

>>    2) Instance of an explicit specialization:

>>       template <class> class B;

>>       template <> struct B<int>;

>>       class B<int> b;

>>

>>    3) Instance of a partial specialization:

>>       template <class> class C;

>>       template <class T> struct C<T*>;

>>       class C<int*> c;

>>

>> By trial and (lots of) error I figured out that in both (1) and (2),

>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>> the template's type_decl.

>>

>> Is there some function to call to get it in (3), or even better,

>> in all three cases?

> 

> I think you're looking for most_general_template.


I don't think that's quite what I'm looking for.  At least it doesn't
return the template or its specialization in all three cases above.
In (2) and (3) it won't distinguish between specializations of B or
C on different types.  In (2), the function returns the same result
for both:

   template <> struct B<int>;
   template <> struct B<char>;

In (3), it similarly returns the same result for both of

   template <class T> struct C<T*>;
   template <class T> struct C<const T>;

even though they are declarations of distinct types.

What I missing?

Martin
Jason Merrill March 10, 2020, 12:08 a.m. | #7
On 3/9/20 5:39 PM, Martin Sebor wrote:
> On 3/9/20 1:40 PM, Jason Merrill wrote:

>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>> issues false positives for those.  According to the reported that's

>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>

>>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>>> positives by relying on the same class_decl_loc_t::class2loc mapping

>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>> detection

>>>>>>> of both issues in template declarations (though more work is still

>>>>>>> needed there).

>>>>>>

>>>>>>> +         a new entry for it and return unless it's a declaration

>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>> +         -Wredundant-tags.  */

>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>> -      return;

>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>> +        return;

>>>>>>

>>>>>> How can the first appearance of a class template be redundant?

>>>>>

>>>>> I'm not sure I correctly understand the question.  The comment says

>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>> a template).  The test case that corresponds to this test is:

>>>>>

>>>>>    template <class> struct S7 { };

>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>

>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>

>>>>> As I mentioned, more work is still needed to handle templates right

>>>>> because some redundant tags are still not diagnosed.  For example:

>>>>>

>>>>>    template <class> struct S7 { };

>>>>>    template <class T>

>>>>>    using U = struct S7<T>;   // missing warning

>>>>

>>>> When we get here for an instance of a template, it doesn't make 

>>>> sense to treat it as a new type.

>>>>

>>>> If decl is a template and type_decl is an instance of that template, 

>>>> do we want to (before the lookup) change type_decl to the template 

>>>> or the corresponding generic TYPE_DECL, which should already be in 

>>>> the table?

>>>

>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>> do I get the corresponding template (or its explicit or partial

>>> specialization) in the three cases below?

>>>

>>>    1) Instance of the primary:

>>>       template <class> class A;

>>>       struct A<int> a;

>>>

>>>    2) Instance of an explicit specialization:

>>>       template <class> class B;

>>>       template <> struct B<int>;

>>>       class B<int> b;

>>>

>>>    3) Instance of a partial specialization:

>>>       template <class> class C;

>>>       template <class T> struct C<T*>;

>>>       class C<int*> c;

>>>

>>> By trial and (lots of) error I figured out that in both (1) and (2),

>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>> the template's type_decl.

>>>

>>> Is there some function to call to get it in (3), or even better,

>>> in all three cases?

>>

>> I think you're looking for most_general_template.

> 

> I don't think that's quite what I'm looking for.  At least it doesn't

> return the template or its specialization in all three cases above.


Ah, true, that function stops at specializations.  Oddly, I don't think 
there's currently a similar function that looks through them.  You could 
create one that does a simple loop through DECL_TI_TEMPLATE like 
is_specialization_of.

> In (2) and (3) it won't distinguish between specializations of B or

> C on different types.  In (2), the function returns the same result

> for both:

> 

>    template <> struct B<int>;

>    template <> struct B<char>;

> 

> In (3), it similarly returns the same result for both of

> 

>    template <class T> struct C<T*>;

>    template <class T> struct C<const T>;

> 

> even though they are declarations of distinct types.



Jason
Andi Kleen via Gcc-patches March 11, 2020, 4:57 p.m. | #8
On 3/9/20 6:08 PM, Jason Merrill wrote:
> On 3/9/20 5:39 PM, Martin Sebor wrote:

>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>>> issues false positives for those.  According to the reported that's

>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>

>>>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>>>> positives by relying on the same class_decl_loc_t::class2loc 

>>>>>>>> mapping

>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>> detection

>>>>>>>> of both issues in template declarations (though more work is still

>>>>>>>> needed there).

>>>>>>>

>>>>>>>> +         a new entry for it and return unless it's a declaration

>>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>> -      return;

>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>> +        return;

>>>>>>>

>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>

>>>>>> I'm not sure I correctly understand the question.  The comment says

>>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>

>>>>>>    template <class> struct S7 { };

>>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>>

>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>

>>>>>> As I mentioned, more work is still needed to handle templates right

>>>>>> because some redundant tags are still not diagnosed.  For example:

>>>>>>

>>>>>>    template <class> struct S7 { };

>>>>>>    template <class T>

>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>

>>>>> When we get here for an instance of a template, it doesn't make 

>>>>> sense to treat it as a new type.

>>>>>

>>>>> If decl is a template and type_decl is an instance of that 

>>>>> template, do we want to (before the lookup) change type_decl to the 

>>>>> template or the corresponding generic TYPE_DECL, which should 

>>>>> already be in the table?

>>>>

>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>>> do I get the corresponding template (or its explicit or partial

>>>> specialization) in the three cases below?

>>>>

>>>>    1) Instance of the primary:

>>>>       template <class> class A;

>>>>       struct A<int> a;

>>>>

>>>>    2) Instance of an explicit specialization:

>>>>       template <class> class B;

>>>>       template <> struct B<int>;

>>>>       class B<int> b;

>>>>

>>>>    3) Instance of a partial specialization:

>>>>       template <class> class C;

>>>>       template <class T> struct C<T*>;

>>>>       class C<int*> c;

>>>>

>>>> By trial and (lots of) error I figured out that in both (1) and (2),

>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>> the template's type_decl.

>>>>

>>>> Is there some function to call to get it in (3), or even better,

>>>> in all three cases?

>>>

>>> I think you're looking for most_general_template.

>>

>> I don't think that's quite what I'm looking for.  At least it doesn't

>> return the template or its specialization in all three cases above.

> 

> Ah, true, that function stops at specializations.  Oddly, I don't think 

> there's currently a similar function that looks through them.  You could 

> create one that does a simple loop through DECL_TI_TEMPLATE like 

> is_specialization_of.


Thanks for the tip.  Even with that I'm having trouble with partial
specializations.  For example in:

   template <class>   struct S;
   template <class T> class S<const T>;
   extern class  S<const int> s1;
   extern struct S<const int> s2;  // expect -Wmismatched-tags

how do I find the declaration of the partial specialization when given
the type in the extern declaration?  A loop in my find_template_for()
function (similar to is_specialization_of) only visits the implicit
specialization S<const int> (i.e., its own type) and the primary.

Martin

> 

>> In (2) and (3) it won't distinguish between specializations of B or

>> C on different types.  In (2), the function returns the same result

>> for both:

>>

>>    template <> struct B<int>;

>>    template <> struct B<char>;

>>

>> In (3), it similarly returns the same result for both of

>>

>>    template <class T> struct C<T*>;

>>    template <class T> struct C<const T>;

>>

>> even though they are declarations of distinct types.

> 

> 

> Jason

>
Andi Kleen via Gcc-patches March 11, 2020, 8:10 p.m. | #9
On 3/11/20 12:57 PM, Martin Sebor wrote:
> On 3/9/20 6:08 PM, Jason Merrill wrote:

>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>>>> issues false positives for those.  According to the reported 

>>>>>>>>> that's

>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>

>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>>>>> positives by relying on the same class_decl_loc_t::class2loc 

>>>>>>>>> mapping

>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>> detection

>>>>>>>>> of both issues in template declarations (though more work is still

>>>>>>>>> needed there).

>>>>>>>>

>>>>>>>>> +         a new entry for it and return unless it's a declaration

>>>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>> -      return;

>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>> +        return;

>>>>>>>>

>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>

>>>>>>> I'm not sure I correctly understand the question.  The comment says

>>>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>

>>>>>>>    template <class> struct S7 { };

>>>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>>>

>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>

>>>>>>> As I mentioned, more work is still needed to handle templates right

>>>>>>> because some redundant tags are still not diagnosed.  For example:

>>>>>>>

>>>>>>>    template <class> struct S7 { };

>>>>>>>    template <class T>

>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>

>>>>>> When we get here for an instance of a template, it doesn't make 

>>>>>> sense to treat it as a new type.

>>>>>>

>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>> template, do we want to (before the lookup) change type_decl to 

>>>>>> the template or the corresponding generic TYPE_DECL, which should 

>>>>>> already be in the table?

>>>>>

>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>>>> do I get the corresponding template (or its explicit or partial

>>>>> specialization) in the three cases below?

>>>>>

>>>>>    1) Instance of the primary:

>>>>>       template <class> class A;

>>>>>       struct A<int> a;

>>>>>

>>>>>    2) Instance of an explicit specialization:

>>>>>       template <class> class B;

>>>>>       template <> struct B<int>;

>>>>>       class B<int> b;

>>>>>

>>>>>    3) Instance of a partial specialization:

>>>>>       template <class> class C;

>>>>>       template <class T> struct C<T*>;

>>>>>       class C<int*> c;

>>>>>

>>>>> By trial and (lots of) error I figured out that in both (1) and (2),

>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>> the template's type_decl.

>>>>>

>>>>> Is there some function to call to get it in (3), or even better,

>>>>> in all three cases?

>>>>

>>>> I think you're looking for most_general_template.

>>>

>>> I don't think that's quite what I'm looking for.  At least it doesn't

>>> return the template or its specialization in all three cases above.

>>

>> Ah, true, that function stops at specializations.  Oddly, I don't 

>> think there's currently a similar function that looks through them.  

>> You could create one that does a simple loop through DECL_TI_TEMPLATE 

>> like is_specialization_of.

> 

> Thanks for the tip.  Even with that I'm having trouble with partial

> specializations.  For example in:

> 

>    template <class>   struct S;

>    template <class T> class S<const T>;

>    extern class  S<const int> s1;

>    extern struct S<const int> s2;  // expect -Wmismatched-tags

> 

> how do I find the declaration of the partial specialization when given

> the type in the extern declaration?  A loop in my find_template_for()

> function (similar to is_specialization_of) only visits the implicit

> specialization S<const int> (i.e., its own type) and the primary.


Is that a problem?  The name is from the primary template, so does it 
matter for this warning whether there's an explicit specialization involved?

> Martin

> 

>>

>>> In (2) and (3) it won't distinguish between specializations of B or

>>> C on different types.  In (2), the function returns the same result

>>> for both:

>>>

>>>    template <> struct B<int>;

>>>    template <> struct B<char>;

>>>

>>> In (3), it similarly returns the same result for both of

>>>

>>>    template <class T> struct C<T*>;

>>>    template <class T> struct C<const T>;

>>>

>>> even though they are declarations of distinct types.

>>

>>

>> Jason

>>

>
Andi Kleen via Gcc-patches March 11, 2020, 9:30 p.m. | #10
On 3/11/20 2:10 PM, Jason Merrill wrote:
> On 3/11/20 12:57 PM, Martin Sebor wrote:

>> On 3/9/20 6:08 PM, Jason Merrill wrote:

>>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>>> -Wredundant-tags doesn't consider type declarations that are also

>>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>>>>> issues false positives for those.  According to the reported 

>>>>>>>>>> that's

>>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>>

>>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>>>>>> positives by relying on the same class_decl_loc_t::class2loc 

>>>>>>>>>> mapping

>>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>>> detection

>>>>>>>>>> of both issues in template declarations (though more work is 

>>>>>>>>>> still

>>>>>>>>>> needed there).

>>>>>>>>>

>>>>>>>>>> +         a new entry for it and return unless it's a declaration

>>>>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>>> -      return;

>>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>>> +        return;

>>>>>>>>>

>>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>>

>>>>>>>> I'm not sure I correctly understand the question.  The comment says

>>>>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>>

>>>>>>>>    template <class> struct S7 { };

>>>>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>>>>

>>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>>

>>>>>>>> As I mentioned, more work is still needed to handle templates right

>>>>>>>> because some redundant tags are still not diagnosed.  For example:

>>>>>>>>

>>>>>>>>    template <class> struct S7 { };

>>>>>>>>    template <class T>

>>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>>

>>>>>>> When we get here for an instance of a template, it doesn't make 

>>>>>>> sense to treat it as a new type.

>>>>>>>

>>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>>> template, do we want to (before the lookup) change type_decl to 

>>>>>>> the template or the corresponding generic TYPE_DECL, which should 

>>>>>>> already be in the table?

>>>>>>

>>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>>>>> do I get the corresponding template (or its explicit or partial

>>>>>> specialization) in the three cases below?

>>>>>>

>>>>>>    1) Instance of the primary:

>>>>>>       template <class> class A;

>>>>>>       struct A<int> a;

>>>>>>

>>>>>>    2) Instance of an explicit specialization:

>>>>>>       template <class> class B;

>>>>>>       template <> struct B<int>;

>>>>>>       class B<int> b;

>>>>>>

>>>>>>    3) Instance of a partial specialization:

>>>>>>       template <class> class C;

>>>>>>       template <class T> struct C<T*>;

>>>>>>       class C<int*> c;

>>>>>>

>>>>>> By trial and (lots of) error I figured out that in both (1) and (2),

>>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>>> the template's type_decl.

>>>>>>

>>>>>> Is there some function to call to get it in (3), or even better,

>>>>>> in all three cases?

>>>>>

>>>>> I think you're looking for most_general_template.

>>>>

>>>> I don't think that's quite what I'm looking for.  At least it doesn't

>>>> return the template or its specialization in all three cases above.

>>>

>>> Ah, true, that function stops at specializations.  Oddly, I don't 

>>> think there's currently a similar function that looks through them. 

>>> You could create one that does a simple loop through DECL_TI_TEMPLATE 

>>> like is_specialization_of.

>>

>> Thanks for the tip.  Even with that I'm having trouble with partial

>> specializations.  For example in:

>>

>>    template <class>   struct S;

>>    template <class T> class S<const T>;

>>    extern class  S<const int> s1;

>>    extern struct S<const int> s2;  // expect -Wmismatched-tags

>>

>> how do I find the declaration of the partial specialization when given

>> the type in the extern declaration?  A loop in my find_template_for()

>> function (similar to is_specialization_of) only visits the implicit

>> specialization S<const int> (i.e., its own type) and the primary.

> 

> Is that a problem?  The name is from the primary template, so does it 

> matter for this warning whether there's an explicit specialization 

> involved?


I don't understand the question.  S<const int> is an instance of
the partial specialization.  To diagnose the right mismatch the warning
needs to know how to find the template (i.e., either the primary, or
the explicit or partial specialization) the instance corresponds to and
the class-key it was declared with.  As it is, while GCC does diagnose
the right declaration (that of s2), it does that thanks to a bug:
because it finds and uses the type and class-key used to declare s1.
If we get rid of s1 it doesn't diagnose anything.

I tried using DECL_TEMPLATE_SPECIALIZATIONS() to get the list of
the partial specializations but it doesn't like any of the arguments
I've given it (it ICEs).

Martin

PS As an aside, both Clang and VC++ have trouble with templates as
well, just slightly different kinds of trouble.   Clang diagnoses
the declaration of the partial specialization while VC++ diagnoses
s1.  In other similar cases like the one below VC++ does the right
thing and Clang is silent.

   template <class>   struct S { };
   template <class T> class S<const T> { };

   template <class T> void f (class S<T>);          // VC++ warns
   template <class T> void g (struct S<const T>);   // GCC & VC++ warn

> 

>> Martin

>>

>>>

>>>> In (2) and (3) it won't distinguish between specializations of B or

>>>> C on different types.  In (2), the function returns the same result

>>>> for both:

>>>>

>>>>    template <> struct B<int>;

>>>>    template <> struct B<char>;

>>>>

>>>> In (3), it similarly returns the same result for both of

>>>>

>>>>    template <class T> struct C<T*>;

>>>>    template <class T> struct C<const T>;

>>>>

>>>> even though they are declarations of distinct types.

>>>

>>>

>>> Jason

>>>

>>

>
Andi Kleen via Gcc-patches March 12, 2020, 5:03 p.m. | #11
On 3/11/20 3:30 PM, Martin Sebor wrote:
> On 3/11/20 2:10 PM, Jason Merrill wrote:

>> On 3/11/20 12:57 PM, Martin Sebor wrote:

>>> On 3/9/20 6:08 PM, Jason Merrill wrote:

>>>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>>>> -Wredundant-tags doesn't consider type declarations that are 

>>>>>>>>>>> also

>>>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>>>>>> issues false positives for those.  According to the reported 

>>>>>>>>>>> that's

>>>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>>>

>>>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these false

>>>>>>>>>>> positives by relying on the same class_decl_loc_t::class2loc 

>>>>>>>>>>> mapping

>>>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>>>> detection

>>>>>>>>>>> of both issues in template declarations (though more work is 

>>>>>>>>>>> still

>>>>>>>>>>> needed there).

>>>>>>>>>>

>>>>>>>>>>> +         a new entry for it and return unless it's a 

>>>>>>>>>>> declaration

>>>>>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>>>> -      return;

>>>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>>>> +        return;

>>>>>>>>>>

>>>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>>>

>>>>>>>>> I'm not sure I correctly understand the question.  The comment 

>>>>>>>>> says

>>>>>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>>>

>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>>>>>

>>>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>>>

>>>>>>>>> As I mentioned, more work is still needed to handle templates 

>>>>>>>>> right

>>>>>>>>> because some redundant tags are still not diagnosed.  For example:

>>>>>>>>>

>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>    template <class T>

>>>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>>>

>>>>>>>> When we get here for an instance of a template, it doesn't make 

>>>>>>>> sense to treat it as a new type.

>>>>>>>>

>>>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>>>> template, do we want to (before the lookup) change type_decl to 

>>>>>>>> the template or the corresponding generic TYPE_DECL, which 

>>>>>>>> should already be in the table?

>>>>>>>

>>>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>>>>>> do I get the corresponding template (or its explicit or partial

>>>>>>> specialization) in the three cases below?

>>>>>>>

>>>>>>>    1) Instance of the primary:

>>>>>>>       template <class> class A;

>>>>>>>       struct A<int> a;

>>>>>>>

>>>>>>>    2) Instance of an explicit specialization:

>>>>>>>       template <class> class B;

>>>>>>>       template <> struct B<int>;

>>>>>>>       class B<int> b;

>>>>>>>

>>>>>>>    3) Instance of a partial specialization:

>>>>>>>       template <class> class C;

>>>>>>>       template <class T> struct C<T*>;

>>>>>>>       class C<int*> c;

>>>>>>>

>>>>>>> By trial and (lots of) error I figured out that in both (1) and (2),

>>>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>>>> the template's type_decl.

>>>>>>>

>>>>>>> Is there some function to call to get it in (3), or even better,

>>>>>>> in all three cases?

>>>>>>

>>>>>> I think you're looking for most_general_template.

>>>>>

>>>>> I don't think that's quite what I'm looking for.  At least it doesn't

>>>>> return the template or its specialization in all three cases above.

>>>>

>>>> Ah, true, that function stops at specializations.  Oddly, I don't 

>>>> think there's currently a similar function that looks through them. 

>>>> You could create one that does a simple loop through 

>>>> DECL_TI_TEMPLATE like is_specialization_of.

>>>

>>> Thanks for the tip.  Even with that I'm having trouble with partial

>>> specializations.  For example in:

>>>

>>>    template <class>   struct S;

>>>    template <class T> class S<const T>;

>>>    extern class  S<const int> s1;

>>>    extern struct S<const int> s2;  // expect -Wmismatched-tags

>>>

>>> how do I find the declaration of the partial specialization when given

>>> the type in the extern declaration?  A loop in my find_template_for()

>>> function (similar to is_specialization_of) only visits the implicit

>>> specialization S<const int> (i.e., its own type) and the primary.

>>

>> Is that a problem?  The name is from the primary template, so does it 

>> matter for this warning whether there's an explicit specialization 

>> involved?

> 

> I don't understand the question.  S<const int> is an instance of

> the partial specialization.  To diagnose the right mismatch the warning

> needs to know how to find the template (i.e., either the primary, or

> the explicit or partial specialization) the instance corresponds to and

> the class-key it was declared with.  As it is, while GCC does diagnose

> the right declaration (that of s2), it does that thanks to a bug:

> because it finds and uses the type and class-key used to declare s1.

> If we get rid of s1 it doesn't diagnose anything.

> 

> I tried using DECL_TEMPLATE_SPECIALIZATIONS() to get the list of

> the partial specializations but it doesn't like any of the arguments

> I've given it (it ICEs).


With this fixed, here's the algorithm I tried:

1) for a type T of a template instantiation (s1 above), get the primary
    P that T was instantiated from using
    P = TYPE_MAIN_DECL (CLASSTYPE_PRIMARY_TEMPLATE_TYPE (T)),
2) from P, get the chain of its specializations using
    SC = DECL_TEMPLATE_SPECIALIZATIONS (P)
3) for each (partial) specialization S on the SC chain get the chain
    of its instantiations IC using DECL_TEMPLATE_INSTANTIATIONS, if
    is_specialization_of (T, TREE_VALUE (IC)) is non-zero take
    TREE_VALUE (SC) as the declaration of the partial specialization
    that the template instanstantiaton T was generated from.

Unfortunately, in the example above, DECL_TEMPLATE_INSTANTIATIONS for
the partial specialization 'class S<const T>' is null (even after all
the declarations have been parsed) so I'm at a dead end.

The other recurring problem I'm running into is that many of the C++
FE macros (and APIs) don't always return the expected/documented result.
I think this is at least in part because until a declaration has been
fully parsed not all the bits are in place to make it possible to tell
if it's an implicit or explicit specialization, for example (so
CLASSTYPE_USE_TEMPLATE (T) is 1 for the first-time declaration of
an explicit specialization, for example).  But in view of the problem
above I'm not sure that's the only reason.

> 

> Martin

> 

> PS As an aside, both Clang and VC++ have trouble with templates as

> well, just slightly different kinds of trouble.   Clang diagnoses

> the declaration of the partial specialization while VC++ diagnoses

> s1.  In other similar cases like the one below VC++ does the right

> thing and Clang is silent.

> 

>    template <class>   struct S { };

>    template <class T> class S<const T> { };

> 

>    template <class T> void f (class S<T>);          // VC++ warns

>    template <class T> void g (struct S<const T>);   // GCC & VC++ warn

> 

>>

>>> Martin

>>>

>>>>

>>>>> In (2) and (3) it won't distinguish between specializations of B or

>>>>> C on different types.  In (2), the function returns the same result

>>>>> for both:

>>>>>

>>>>>    template <> struct B<int>;

>>>>>    template <> struct B<char>;

>>>>>

>>>>> In (3), it similarly returns the same result for both of

>>>>>

>>>>>    template <class T> struct C<T*>;

>>>>>    template <class T> struct C<const T>;

>>>>>

>>>>> even though they are declarations of distinct types.

>>>>

>>>>

>>>> Jason

>>>>

>>>

>>

>
Andi Kleen via Gcc-patches March 12, 2020, 10:38 p.m. | #12
On 3/12/20 11:03 AM, Martin Sebor wrote:
> On 3/11/20 3:30 PM, Martin Sebor wrote:

>> On 3/11/20 2:10 PM, Jason Merrill wrote:

>>> On 3/11/20 12:57 PM, Martin Sebor wrote:

>>>> On 3/9/20 6:08 PM, Jason Merrill wrote:

>>>>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>>>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>>>>> -Wredundant-tags doesn't consider type declarations that are 

>>>>>>>>>>>> also

>>>>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' and

>>>>>>>>>>>> issues false positives for those.  According to the reported 

>>>>>>>>>>>> that's

>>>>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>>>>

>>>>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these 

>>>>>>>>>>>> false

>>>>>>>>>>>> positives by relying on the same class_decl_loc_t::class2loc 

>>>>>>>>>>>> mapping

>>>>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>>>>> detection

>>>>>>>>>>>> of both issues in template declarations (though more work is 

>>>>>>>>>>>> still

>>>>>>>>>>>> needed there).

>>>>>>>>>>>

>>>>>>>>>>>> +         a new entry for it and return unless it's a 

>>>>>>>>>>>> declaration

>>>>>>>>>>>> +         involving a template that may need to be diagnosed by

>>>>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>>>>> -      return;

>>>>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>>>>> +        return;

>>>>>>>>>>>

>>>>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>>>>

>>>>>>>>>> I'm not sure I correctly understand the question.  The comment 

>>>>>>>>>> says

>>>>>>>>>> "involving a template" (i.e., not one of the first declaration of

>>>>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>>>>

>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>    struct S7<void> s7v;  // { dg-warning "\\\[-Wredundant-tags" }

>>>>>>>>>>

>>>>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>>>>

>>>>>>>>>> As I mentioned, more work is still needed to handle templates 

>>>>>>>>>> right

>>>>>>>>>> because some redundant tags are still not diagnosed.  For 

>>>>>>>>>> example:

>>>>>>>>>>

>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>    template <class T>

>>>>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>>>>

>>>>>>>>> When we get here for an instance of a template, it doesn't make 

>>>>>>>>> sense to treat it as a new type.

>>>>>>>>>

>>>>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>>>>> template, do we want to (before the lookup) change type_decl to 

>>>>>>>>> the template or the corresponding generic TYPE_DECL, which 

>>>>>>>>> should already be in the table?

>>>>>>>>

>>>>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) and

>>>>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, how

>>>>>>>> do I get the corresponding template (or its explicit or partial

>>>>>>>> specialization) in the three cases below?

>>>>>>>>

>>>>>>>>    1) Instance of the primary:

>>>>>>>>       template <class> class A;

>>>>>>>>       struct A<int> a;

>>>>>>>>

>>>>>>>>    2) Instance of an explicit specialization:

>>>>>>>>       template <class> class B;

>>>>>>>>       template <> struct B<int>;

>>>>>>>>       class B<int> b;

>>>>>>>>

>>>>>>>>    3) Instance of a partial specialization:

>>>>>>>>       template <class> class C;

>>>>>>>>       template <class T> struct C<T*>;

>>>>>>>>       class C<int*> c;

>>>>>>>>

>>>>>>>> By trial and (lots of) error I figured out that in both (1) and 

>>>>>>>> (2),

>>>>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>>>>> the template's type_decl.

>>>>>>>>

>>>>>>>> Is there some function to call to get it in (3), or even better,

>>>>>>>> in all three cases?

>>>>>>>

>>>>>>> I think you're looking for most_general_template.

>>>>>>

>>>>>> I don't think that's quite what I'm looking for.  At least it doesn't

>>>>>> return the template or its specialization in all three cases above.

>>>>>

>>>>> Ah, true, that function stops at specializations.  Oddly, I don't 

>>>>> think there's currently a similar function that looks through them. 

>>>>> You could create one that does a simple loop through 

>>>>> DECL_TI_TEMPLATE like is_specialization_of.

>>>>

>>>> Thanks for the tip.  Even with that I'm having trouble with partial

>>>> specializations.  For example in:

>>>>

>>>>    template <class>   struct S;

>>>>    template <class T> class S<const T>;

>>>>    extern class  S<const int> s1;

>>>>    extern struct S<const int> s2;  // expect -Wmismatched-tags

>>>>

>>>> how do I find the declaration of the partial specialization when given

>>>> the type in the extern declaration?  A loop in my find_template_for()

>>>> function (similar to is_specialization_of) only visits the implicit

>>>> specialization S<const int> (i.e., its own type) and the primary.

>>>

>>> Is that a problem?  The name is from the primary template, so does it 

>>> matter for this warning whether there's an explicit specialization 

>>> involved?

>>

>> I don't understand the question.  S<const int> is an instance of

>> the partial specialization.  To diagnose the right mismatch the warning

>> needs to know how to find the template (i.e., either the primary, or

>> the explicit or partial specialization) the instance corresponds to and

>> the class-key it was declared with.  As it is, while GCC does diagnose

>> the right declaration (that of s2), it does that thanks to a bug:

>> because it finds and uses the type and class-key used to declare s1.

>> If we get rid of s1 it doesn't diagnose anything.

>>

>> I tried using DECL_TEMPLATE_SPECIALIZATIONS() to get the list of

>> the partial specializations but it doesn't like any of the arguments

>> I've given it (it ICEs).

> 

> With this fixed, here's the algorithm I tried:

> 

> 1) for a type T of a template instantiation (s1 above), get the primary

>     P that T was instantiated from using

>     P = TYPE_MAIN_DECL (CLASSTYPE_PRIMARY_TEMPLATE_TYPE (T)),

> 2) from P, get the chain of its specializations using

>     SC = DECL_TEMPLATE_SPECIALIZATIONS (P)

> 3) for each (partial) specialization S on the SC chain get the chain

>     of its instantiations IC using DECL_TEMPLATE_INSTANTIATIONS, if

>     is_specialization_of (T, TREE_VALUE (IC)) is non-zero take

>     TREE_VALUE (SC) as the declaration of the partial specialization

>     that the template instanstantiaton T was generated from.

> 

> Unfortunately, in the example above, DECL_TEMPLATE_INSTANTIATIONS for

> the partial specialization 'class S<const T>' is null (even after all

> the declarations have been parsed) so I'm at a dead end.


After a lot more trial and error I discovered
most_specialized_partial_spec in pt.c with whose help I have been able
to get templates to work the way I think they should (at least the cases
I've tested do).

Besides fixing the original problem that motivated it, the attached
patch also corrects how template specializations are handled: the first
declaration of either a primary template or its specialization (either
explicit or partial) determines the class-key in subsequent uses of
the type or its instantiations.  To do this for uses of first-time
template instantiations such as in the declaration of s1 in the test
case above, class_decl_loc_t::diag_mismatched_tags looks up the template
(either the primary or the partial specialization) in the CLASS2LOC map
and uses it and its class-key as the guide when issuing diagnostics.
This also means that the first instance of every template needs to
have a record in the CLASS2LOC map (it also needs it to compare its
class-key to subsequent redeclarations of the template).

This has been unexpectedly difficult.  A big part of it is that I've
never before worked with templates in the front-end so I had to learn
how they're organized (I'm far from having mastered it).  What's made
the learning curve especially steep, besides the sparse documentation,
is the problems hinted at in the paragraph below below.  This whole
area could really stand to be documented in some sort of a writeup:
a high-level overview of how templates are put together (i.e., what
hangs off what in the tree representation) and what APIs to use to
work with them.

The revised patch has been tested on x86_64-linux.

Martin


> The other recurring problem I'm running into is that many of the C++

> FE macros (and APIs) don't always return the expected/documented result.

> I think this is at least in part because until a declaration has been

> fully parsed not all the bits are in place to make it possible to tell

> if it's an implicit or explicit specialization, for example (so

> CLASSTYPE_USE_TEMPLATE (T) is 1 for the first-time declaration of

> an explicit specialization, for example).  But in view of the problem

> above I'm not sure that's the only reason.

> 

>>

>> Martin

>>

>> PS As an aside, both Clang and VC++ have trouble with templates as

>> well, just slightly different kinds of trouble.   Clang diagnoses

>> the declaration of the partial specialization while VC++ diagnoses

>> s1.  In other similar cases like the one below VC++ does the right

>> thing and Clang is silent.

>>

>>    template <class>   struct S { };

>>    template <class T> class S<const T> { };

>>

>>    template <class T> void f (class S<T>);          // VC++ warns

>>    template <class T> void g (struct S<const T>);   // GCC & VC++ warn

>>

>>>

>>>> Martin

>>>>

>>>>>

>>>>>> In (2) and (3) it won't distinguish between specializations of B or

>>>>>> C on different types.  In (2), the function returns the same result

>>>>>> for both:

>>>>>>

>>>>>>    template <> struct B<int>;

>>>>>>    template <> struct B<char>;

>>>>>>

>>>>>> In (3), it similarly returns the same result for both of

>>>>>>

>>>>>>    template <class T> struct C<T*>;

>>>>>>    template <class T> struct C<const T>;

>>>>>>

>>>>>> even though they are declarations of distinct types.

>>>>>

>>>>>

>>>>> Jason

>>>>>

>>>>

>>>

>>

>
PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/93824
	PR c++/93810
	* cp-tree.h (most_specialized_partial_spec): Declare.
	* parser.c (cp_parser_elaborated_type_specifier): Update a comment.
	(class_decl_loc_t::class_decl_loc_t): Add an argument.
	(class_decl_loc_t::add): Same.
	(class_decl_loc_t::add_or_diag_mismatched_tag): Same.
	(class_decl_loc_t::is_decl): New member function.
	(class_decl_loc_t::class_key_loc_t): Add a new member.
	(specialization_of): New function.
	(cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	Correct handling of template specializations.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.  Use primary template or partial
	specialization as the guide for uses of implicit instantiations.
	* pt.c (most_specialized_partial_spec): Declare extern.

gcc/testsuite/ChangeLog:

	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wmismatched-tags-4.C: New test.
	* g++.dg/warn/Wmismatched-tags-5.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0a7381cee3f..436e4a27fe3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6943,6 +6943,7 @@ extern int comp_template_args			(tree, tree, tree * = NULL,
 extern int template_args_equal                  (tree, tree, bool = false);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
+extern tree most_specialized_partial_spec       (tree, tsubst_flags_t);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, int, tree, tree,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 24f71671469..eae1c1503c4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18937,7 +18937,10 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
     cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
   else
     {
-      /* Diagnose class/struct/union mismatches.  */
+      /* Diagnose class/struct/union mismatches.
+	 FIXME: Relying on cp_parser_declares_only_class_p to diffeerentiate
+	 declarations of a class from its uses doesn't work for type aliases
+	 (as in using T = class C;).  */
       cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
 				 cp_parser_declares_only_class_p (parser));
 
@@ -30885,12 +30888,12 @@ class class_decl_loc_t
      DEF_P is true for a class declaration that is a definition.
      CURLOC is the associated location.  */
   class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
-		    location_t curloc = input_location)
+		    bool decl_p, location_t curloc = input_location)
     : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
   {
     locvec.create (4);
     class_key_loc_t ckl (current_function_decl, curloc, class_key,
-			 key_redundant);
+			 key_redundant, def_p || decl_p);
     locvec.quick_push (ckl);
   }
 
@@ -30924,12 +30927,13 @@ class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
-  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool, bool);
 
 private:
 
@@ -30948,6 +30952,11 @@ private:
     return locvec[i].key_redundant;
   }
 
+  bool is_decl (unsigned i) const
+  {
+    return locvec[i].is_decl;
+  }
+
   tag_types class_key (unsigned i) const
   {
     return locvec[i].class_key;
@@ -30957,8 +30966,10 @@ private:
      class-key.  */
   struct class_key_loc_t
   {
-    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
-      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant,
+		     bool decl)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant),
+	is_decl (decl) { }
 
     /* The function the type is mentioned in.  */
     tree func;
@@ -30968,7 +30979,11 @@ private:
     tag_types class_key;
     /* True when the class-key could be omitted at this location
        without an ambiguity with another symbol of the same name.  */
-    bool key_redundant;
+    bool key_redundant: 1;
+    /* True for an entry that corresponds to a dedicated declaration
+       (including a definition) of a class, false for its other uses
+       (such as in declarations of other entities).  */
+    bool is_decl: 1;
   };
   /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
   vec <class_key_loc_t> locvec;
@@ -31021,6 +31036,64 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Returns the template or specialization of one to which the RECORD_TYPE
+   DECL corresponsds.  IS_USE should be set when DECL refers to a class
+   that's being used as opposed to being declared.  In that case, if
+   the specialization is the same as itself, returns the most specialized
+   partial specialization of the template to which it corresponds if one
+   exists, or the primary template otherwise.  */
+
+static tree
+specialization_of (tree decl, bool is_use = false)
+{
+  gcc_assert (TREE_CODE (decl) == TYPE_DECL);
+  tree decl_type = TREE_TYPE (decl);
+
+  tree ret = decl_type;
+
+  for (tree t = decl_type;
+       t != NULL_TREE;
+       t = CLASSTYPE_USE_TEMPLATE (t)
+	 ? TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) : NULL_TREE
+       )
+    {
+      if (same_type_ignoring_top_level_qualifiers_p (t, decl_type))
+	{
+	  ret = t;
+	  break;
+	}
+    }
+
+  if (ret == decl_type && is_use)
+    { /* RET is the first specialization of the type; since its also
+	 a USE of it, it's the first implicit instantiation of it.
+	 Determine the template or its partial specialization to which
+	 it corresponds.  */
+      if (tree spec = most_specialized_partial_spec (ret, tsubst_flags_t ()))
+	if (spec != error_mark_node)
+	  ret = TREE_VALUE (spec);
+
+      if (ret == decl_type)
+	ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (decl_type);
+    }
+
+  return ret;
+}
+
+
+/* Adds the class TYPE to the collection of class decls and diagnoses
+   redundant tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a (likely, based on syntactic context) declaration of class
+   TYPE and clear for a reference to it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31032,7 +31105,10 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31050,29 +31126,19 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
+  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)
     {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
+      /* When TYPE is the use of an implicit specialization of a previously
+	 declared template set TYPE_DECL to the type of the primary template
+	 for the specialization and look it up in CLASS2LOC below.  For uses
+	 of explicit or partial specializations TYPE_DECL already points to
+	 the declaration of the specialization.  */
+      type_decl = specialization_of (type_decl);
+      type_decl = TYPE_MAIN_DECL (type_decl);
     }
 
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it
+     must exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31082,30 +31148,52 @@ class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
-	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
+	  *rdl = class_decl_loc_t (class_key, false, def_p, decl_p);
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled header.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant,
+				   def_p, decl_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31115,7 +31203,7 @@ void
 class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 					      tag_types class_key,
 					      bool redundant,
-					      bool def_p)
+					      bool def_p, bool decl_p)
 {
   /* Reset the CLASS_KEY associated with this type on mismatch.
      This is an optimization that lets the diagnostic code skip
@@ -31130,7 +31218,7 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 
   /* Append a record of this declaration to the vector.  */
   class_key_loc_t ckl (current_function_decl, input_location, class_key,
-		       redundant);
+		       redundant, def_p || decl_p);
   locvec.safe_push (ckl);
 
   if (idxdef == UINT_MAX)
@@ -31158,35 +31246,76 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
-  unsigned ndecls = locvec.length ();
-
-  /* Skip a declaration that consistently uses the same class-key
-     or one with just a solitary declaration (i.e., TYPE_DECL).  */
-  if (def_class_key != none_type || ndecls < 2)
+  if (!warn_mismatched_tags)
     return;
 
-  /* Save the current function before changing it below.  */
-  tree save_func = current_function_decl;
-  /* Set if a class definition for RECLOC has been seen.  */
+  /* Number of usesn of the class.  */
+  unsigned ndecls = locvec.length ();
+
+  /* Set if a definition for the class has been seen.  */
   bool def_p = idxdef < ndecls;
-  unsigned idxguide = def_p ? idxdef : 0;
+
+  /* The class (or template) declaration guiding the decisions about
+     the diagnostic.  For ordinary classes it's the same as THIS.  For
+     uses of instantiations of templates other than their declarations
+     it points to the record for the declaration of the corresponding
+     primary template or partial specialization.  */
+  class_decl_loc_t *cdlguide = this;
+
+  tree type = TREE_TYPE (type_decl);
+  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))
+    {
+      /* For implicit instantiations of a primary template look up
+	 up the primary or partial specialization and use it as
+	 the expected class-key rather than using the class-key of
+	 the first reference to the instantiation.  The primary must
+	 be (and inevitably is) at index zero.  */
+      tree pt = specialization_of (TYPE_MAIN_DECL (type), true);
+      if (TREE_CODE (pt) == TEMPLATE_DECL)
+	pt = TREE_TYPE (pt);
+      pt = TYPE_MAIN_DECL (pt);
+      cdlguide = class2loc.get (pt);
+      gcc_assert (cdlguide != NULL);
+    }
+  else
+    {
+      /* Skip a declaration that consistently uses the same class-key
+	 or one with just a solitary declaration (i.e., TYPE_DECL).  */
+      if (def_class_key != none_type || ndecls < 2)
+	return;
+    }
+
+  /* The index of the declaration whose class-key this declaration
+     is expected to match.  It's either the class-key of the class
+     definintion if one exists or the first declaration otherwise.  */
+  const unsigned idxguide = def_p ? idxdef : 0;
   unsigned idx = 0;
   /* Advance IDX to the first declaration that either is not
      a definition or that doesn't match the first declaration
      if no definition is provided.  */
-  while (class_key (idx) == class_key (idxguide))
+  while (class_key (idx) == cdlguide->class_key (idxguide))
     if (++idx == ndecls)
       return;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
-     if no definition has been provided.  */
-  tag_types xpect_key = class_key (def_p ? idxguide : 0);
-  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
-  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+     if no definition has been provided.
+     For implicit instantiations of a primary template it's
+     the class-key used to declare the primary with.  The primary
+     must be at index zero.  */
+  const tag_types xpect_key
+    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
+  if (cdlguide != this && xpect_key == class_key (idx))
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
   /* Set the function declaration to print in diagnostic context.  */
   current_function_decl = function (idx);
 
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+
   location_t loc = location (idx);
   bool key_redundant_p = key_redundant (idx);
   auto_diagnostic_group d;
@@ -31207,7 +31336,7 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Also point to the first declaration or definition that guided
      the decision to issue the warning above.  */
-  inform (location (idxguide),
+  inform (cdlguide->location (idxguide),
 	  (def_p
 	   ? G_("%qT defined as %qs here")
 	   : G_("%qT first declared as %qs here")),
@@ -31249,20 +31378,26 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
   /* Save the current function before changing it below.  It should
      be null at this point.  */
   tree save_func = current_function_decl;
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct/template declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index cb237ba0d9d..ef232ca4fb1 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -185,7 +185,7 @@ static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static tree most_specialized_partial_spec (tree, tsubst_flags_t);
+tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@@ -24317,7 +24317,7 @@ most_general_template (tree decl)
    partial specializations matching TARGET, then NULL_TREE is
    returned, indicating that the primary template should be used.  */
 
-static tree
+tree
 most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 {
   tree list = NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
new file mode 100644
index 00000000000..7570264b1d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
@@ -0,0 +1,141 @@
+/* PR c++/94078 - bogus and missing -Wmismatched-tags on an instance
+   of a template
+   Verify that -Wmismatched-tags is issued for redeclarations and
+   instances of the appropriate primary template or specialization.
+  { dg-do compile }
+  { dg-options "-Wmismatched-tags" } */
+
+// Exercise explicit specialization.
+template <class> class S1;
+template <>      struct S1<int>;
+
+template <class> class S1;
+template <class> struct S1;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<char>;
+template <>      struct S1<char>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<int>;       // { dg-warning "\\\[-Wmismatched-tags" }
+template <>      struct S1<int>;
+
+extern        S1<void> s1v;
+extern class  S1<void> s1v;
+extern struct S1<void> s1v;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S1<int> s1i;
+extern class  S1<int> s1i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S1<int> s1i;
+
+extern        S1<char> s1c;
+extern class  S1<char> s1c;
+extern struct S1<char> s1c;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+// Exercise partial specialization.
+template <class>   struct S2;
+template <class T> class S2<const T>;
+
+template <class>   class S2;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S2;
+
+template <class T> class S2<const T>;
+template <class T> struct S2<const T>;// { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S2<int> s2i;
+extern class  S2<int> s2i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S2<int> s2i;
+
+extern        S2<const int> s2ci;
+extern class  S2<const int> s2ci;
+extern struct S2<const int> s2ci;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+template <class>   struct S3;
+template <class T> class S3<T*>;
+template <class T> struct S3<T&>;
+
+template <class>   class S3;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S3;
+
+template <class T> class S3<T*>;
+template <class T> struct S3<T*>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <class T> class S3<T&>;      // { dg-warning "\\\[-Wmismatched-tags" }
+template <class T> struct S3<T&>;
+
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat exactly the same as above.
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat the same as above just with different type.
+extern        S3<long> s3l;
+extern class  S3<long> s3l;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long> s3l;
+
+extern        S3<long*> s3lp;
+extern class  S3<long*> s3lp;
+extern struct S3<long*> s3lp;         // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long&> s3lr;
+extern class  S3<long&> s3lr;         // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long&> s3lr;
+
+// Repeat with the class-keys swapped.
+extern        S3<long> s3l;
+extern struct S3<long> s3l;
+extern class  S3<long> s3l;          // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long*> s3lp;
+extern struct S3<long*> s3lp;        // { dg-warning "\\\[-Wmismatched-tags" }
+extern class  S3<long*> s3lp;
+
+extern        S3<long&> s3lr;
+extern struct S3<long&> s3lr;
+extern class  S3<long&> s3lr;        // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+namespace N
+{
+template <class> struct A;
+
+extern class A<int> ai;               // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct A<int> ai;
+
+typedef class A<int> AI;              // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct A<int> AI;
+
+template <class> struct B;
+template <> class B<int>;
+template <> struct B<char>;
+
+extern class B<int> bi;
+extern struct B<int> bi;              // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern class B<char> bc;              // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct B<char> bc;
+
+typedef class B<char> BC;             // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct B<char> BC;
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
new file mode 100644
index 00000000000..7ab2fbbf55a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
@@ -0,0 +1,78 @@
+/* PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef
+   of an implicit class template specialization
+  { dg-do compile }
+  { dg-options "-Wall -Wmismatched-tags" }
+  { dg-require-effective-target c++11 } */
+
+class A;                                // { dg-message "declared as 'class'" }
+typedef        A A0;
+typedef class  A A0;
+typedef struct A A0;                    // { dg-warning "-Wmismatched-tags" }
+
+template <int> struct B;                // { dg-message "declared as 'struct'" }
+typedef        B<0> B0;
+typedef class  B<0> B0;                 // { dg-warning "-Wmismatched-tags" }
+typedef struct B<0> B0;
+
+template <int> struct C;                // { dg-message "declared as 'struct'" }
+
+template <int N>
+struct X_CNp1 {
+  typedef C<N + 1> CNp1;
+};
+
+template <int N>
+struct X_class_CNp1 {
+  typedef class C<N + 1> CNp1;          // { dg-warning "-Wmismatched-tags" }
+};
+
+template <int N>
+struct X_struct_CNp1 {
+  typedef struct C<N + 1> CNp1;
+};
+
+template <class>   class  D;
+template <class T> struct D<T*>;
+template <class T> class  D<T&>;
+template <class T> struct D<const T*>;
+template <class T> class  D<const T&>;
+template <class T> struct D<volatile T*>;
+template <class T> class  D<volatile T&>;
+template <class T> struct D<const volatile T*>;
+template <class T> class  D<const volatile T&>;
+
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+
+typedef class  D<int&>  DIR;
+typedef struct D<int&> DIR;             // { dg-warning "-Wmismatched-tags" }
+typedef class  D<int&>  DIR;
+
+
+typedef struct D<const int*> DCIP;
+typedef class  D<const int*> DCIP;      // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const int*> DCIP;
+
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const int&>  DCIR;
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<volatile int*> DVIP;
+typedef class  D<volatile int*> DVIP;   // { dg-warning "-Wmismatched-tags" }
+typedef struct D<volatile int*> DVIP;
+
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+typedef class  D<volatile int&> DVIR;
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<const volatile int*> DCVIP;
+typedef class  D<const volatile int*> DCVIP;    // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const volatile int*> DCVIP;
+
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const volatile int&> DCVIR;
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@ union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..ce5cb967b79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,91 @@
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 T_S3;           // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 T_S4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <int> struct TS1;
+
+// Verify redeclaration with no definition.
+template <> struct TS1<0>;
+template <> struct TS1<0>;
+
+// Verify definition after a declaration and vice versa.
+template <> struct TS1<1>;
+template <> struct TS1<1> { };
+template <> struct TS1<1>;
+
+// Verify typedefs of an explicit specialization with a definition.
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<1> TS1_1;
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an expplicit specialization.
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<1> ts1_1;
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify typedefs of an implicit specialization without a definition.
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<2> TS1_2;
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an implicit specialization.
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<2> ts1_2;
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+
+
+// Verify partial template specialization.
+template <class> struct TS2;
+template <class T> struct TS2<const T>;
+template <class T> struct TS2<volatile T>;
+
+template <class T>
+struct TS4
+{
+  typedef struct TS2<const T> TS2_CT1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<const T> TS2_CT2;
+
+  typedef struct TS2<T> TS2_T1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<T> TS2_T2;
+};
Andi Kleen via Gcc-patches March 18, 2020, 10:09 p.m. | #13
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-March/541962.html

On 3/12/20 4:38 PM, Martin Sebor wrote:
> On 3/12/20 11:03 AM, Martin Sebor wrote:

>> On 3/11/20 3:30 PM, Martin Sebor wrote:

>>> On 3/11/20 2:10 PM, Jason Merrill wrote:

>>>> On 3/11/20 12:57 PM, Martin Sebor wrote:

>>>>> On 3/9/20 6:08 PM, Jason Merrill wrote:

>>>>>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>>>>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>>>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>>>>>> -Wredundant-tags doesn't consider type declarations that 

>>>>>>>>>>>>> are also

>>>>>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' 

>>>>>>>>>>>>> and

>>>>>>>>>>>>> issues false positives for those.  According to the 

>>>>>>>>>>>>> reported that's

>>>>>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>>>>>

>>>>>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these 

>>>>>>>>>>>>> false

>>>>>>>>>>>>> positives by relying on the same 

>>>>>>>>>>>>> class_decl_loc_t::class2loc mapping

>>>>>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>>>>>> detection

>>>>>>>>>>>>> of both issues in template declarations (though more work 

>>>>>>>>>>>>> is still

>>>>>>>>>>>>> needed there).

>>>>>>>>>>>>

>>>>>>>>>>>>> +         a new entry for it and return unless it's a 

>>>>>>>>>>>>> declaration

>>>>>>>>>>>>> +         involving a template that may need to be 

>>>>>>>>>>>>> diagnosed by

>>>>>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>>>>>> -      return;

>>>>>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>>>>>> +        return;

>>>>>>>>>>>>

>>>>>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>>>>>

>>>>>>>>>>> I'm not sure I correctly understand the question.  The 

>>>>>>>>>>> comment says

>>>>>>>>>>> "involving a template" (i.e., not one of the first 

>>>>>>>>>>> declaration of

>>>>>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>>>>>

>>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>>    struct S7<void> s7v;  // { dg-warning 

>>>>>>>>>>> "\\\[-Wredundant-tags" }

>>>>>>>>>>>

>>>>>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>>>>>

>>>>>>>>>>> As I mentioned, more work is still needed to handle templates 

>>>>>>>>>>> right

>>>>>>>>>>> because some redundant tags are still not diagnosed.  For 

>>>>>>>>>>> example:

>>>>>>>>>>>

>>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>>    template <class T>

>>>>>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>>>>>

>>>>>>>>>> When we get here for an instance of a template, it doesn't 

>>>>>>>>>> make sense to treat it as a new type.

>>>>>>>>>>

>>>>>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>>>>>> template, do we want to (before the lookup) change type_decl 

>>>>>>>>>> to the template or the corresponding generic TYPE_DECL, which 

>>>>>>>>>> should already be in the table?

>>>>>>>>>

>>>>>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) 

>>>>>>>>> and

>>>>>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, 

>>>>>>>>> how

>>>>>>>>> do I get the corresponding template (or its explicit or partial

>>>>>>>>> specialization) in the three cases below?

>>>>>>>>>

>>>>>>>>>    1) Instance of the primary:

>>>>>>>>>       template <class> class A;

>>>>>>>>>       struct A<int> a;

>>>>>>>>>

>>>>>>>>>    2) Instance of an explicit specialization:

>>>>>>>>>       template <class> class B;

>>>>>>>>>       template <> struct B<int>;

>>>>>>>>>       class B<int> b;

>>>>>>>>>

>>>>>>>>>    3) Instance of a partial specialization:

>>>>>>>>>       template <class> class C;

>>>>>>>>>       template <class T> struct C<T*>;

>>>>>>>>>       class C<int*> c;

>>>>>>>>>

>>>>>>>>> By trial and (lots of) error I figured out that in both (1) and 

>>>>>>>>> (2),

>>>>>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>>>>>> the template's type_decl.

>>>>>>>>>

>>>>>>>>> Is there some function to call to get it in (3), or even better,

>>>>>>>>> in all three cases?

>>>>>>>>

>>>>>>>> I think you're looking for most_general_template.

>>>>>>>

>>>>>>> I don't think that's quite what I'm looking for.  At least it 

>>>>>>> doesn't

>>>>>>> return the template or its specialization in all three cases above.

>>>>>>

>>>>>> Ah, true, that function stops at specializations.  Oddly, I don't 

>>>>>> think there's currently a similar function that looks through 

>>>>>> them. You could create one that does a simple loop through 

>>>>>> DECL_TI_TEMPLATE like is_specialization_of.

>>>>>

>>>>> Thanks for the tip.  Even with that I'm having trouble with partial

>>>>> specializations.  For example in:

>>>>>

>>>>>    template <class>   struct S;

>>>>>    template <class T> class S<const T>;

>>>>>    extern class  S<const int> s1;

>>>>>    extern struct S<const int> s2;  // expect -Wmismatched-tags

>>>>>

>>>>> how do I find the declaration of the partial specialization when given

>>>>> the type in the extern declaration?  A loop in my find_template_for()

>>>>> function (similar to is_specialization_of) only visits the implicit

>>>>> specialization S<const int> (i.e., its own type) and the primary.

>>>>

>>>> Is that a problem?  The name is from the primary template, so does 

>>>> it matter for this warning whether there's an explicit 

>>>> specialization involved?

>>>

>>> I don't understand the question.  S<const int> is an instance of

>>> the partial specialization.  To diagnose the right mismatch the warning

>>> needs to know how to find the template (i.e., either the primary, or

>>> the explicit or partial specialization) the instance corresponds to and

>>> the class-key it was declared with.  As it is, while GCC does diagnose

>>> the right declaration (that of s2), it does that thanks to a bug:

>>> because it finds and uses the type and class-key used to declare s1.

>>> If we get rid of s1 it doesn't diagnose anything.

>>>

>>> I tried using DECL_TEMPLATE_SPECIALIZATIONS() to get the list of

>>> the partial specializations but it doesn't like any of the arguments

>>> I've given it (it ICEs).

>>

>> With this fixed, here's the algorithm I tried:

>>

>> 1) for a type T of a template instantiation (s1 above), get the primary

>>     P that T was instantiated from using

>>     P = TYPE_MAIN_DECL (CLASSTYPE_PRIMARY_TEMPLATE_TYPE (T)),

>> 2) from P, get the chain of its specializations using

>>     SC = DECL_TEMPLATE_SPECIALIZATIONS (P)

>> 3) for each (partial) specialization S on the SC chain get the chain

>>     of its instantiations IC using DECL_TEMPLATE_INSTANTIATIONS, if

>>     is_specialization_of (T, TREE_VALUE (IC)) is non-zero take

>>     TREE_VALUE (SC) as the declaration of the partial specialization

>>     that the template instanstantiaton T was generated from.

>>

>> Unfortunately, in the example above, DECL_TEMPLATE_INSTANTIATIONS for

>> the partial specialization 'class S<const T>' is null (even after all

>> the declarations have been parsed) so I'm at a dead end.

> 

> After a lot more trial and error I discovered

> most_specialized_partial_spec in pt.c with whose help I have been able

> to get templates to work the way I think they should (at least the cases

> I've tested do).

> 

> Besides fixing the original problem that motivated it, the attached

> patch also corrects how template specializations are handled: the first

> declaration of either a primary template or its specialization (either

> explicit or partial) determines the class-key in subsequent uses of

> the type or its instantiations.  To do this for uses of first-time

> template instantiations such as in the declaration of s1 in the test

> case above, class_decl_loc_t::diag_mismatched_tags looks up the template

> (either the primary or the partial specialization) in the CLASS2LOC map

> and uses it and its class-key as the guide when issuing diagnostics.

> This also means that the first instance of every template needs to

> have a record in the CLASS2LOC map (it also needs it to compare its

> class-key to subsequent redeclarations of the template).

> 

> This has been unexpectedly difficult.  A big part of it is that I've

> never before worked with templates in the front-end so I had to learn

> how they're organized (I'm far from having mastered it).  What's made

> the learning curve especially steep, besides the sparse documentation,

> is the problems hinted at in the paragraph below below.  This whole

> area could really stand to be documented in some sort of a writeup:

> a high-level overview of how templates are put together (i.e., what

> hangs off what in the tree representation) and what APIs to use to

> work with them.

> 

> The revised patch has been tested on x86_64-linux.

> 

> Martin

> 

> 

>> The other recurring problem I'm running into is that many of the C++

>> FE macros (and APIs) don't always return the expected/documented result.

>> I think this is at least in part because until a declaration has been

>> fully parsed not all the bits are in place to make it possible to tell

>> if it's an implicit or explicit specialization, for example (so

>> CLASSTYPE_USE_TEMPLATE (T) is 1 for the first-time declaration of

>> an explicit specialization, for example).  But in view of the problem

>> above I'm not sure that's the only reason.

>>

>>>

>>> Martin

>>>

>>> PS As an aside, both Clang and VC++ have trouble with templates as

>>> well, just slightly different kinds of trouble.   Clang diagnoses

>>> the declaration of the partial specialization while VC++ diagnoses

>>> s1.  In other similar cases like the one below VC++ does the right

>>> thing and Clang is silent.

>>>

>>>    template <class>   struct S { };

>>>    template <class T> class S<const T> { };

>>>

>>>    template <class T> void f (class S<T>);          // VC++ warns

>>>    template <class T> void g (struct S<const T>);   // GCC & VC++ warn

>>>

>>>>

>>>>> Martin

>>>>>

>>>>>>

>>>>>>> In (2) and (3) it won't distinguish between specializations of B or

>>>>>>> C on different types.  In (2), the function returns the same result

>>>>>>> for both:

>>>>>>>

>>>>>>>    template <> struct B<int>;

>>>>>>>    template <> struct B<char>;

>>>>>>>

>>>>>>> In (3), it similarly returns the same result for both of

>>>>>>>

>>>>>>>    template <class T> struct C<T*>;

>>>>>>>    template <class T> struct C<const T>;

>>>>>>>

>>>>>>> even though they are declarations of distinct types.

>>>>>>

>>>>>>

>>>>>> Jason

>>>>>>

>>>>>

>>>>

>>>

>>

>
Andi Kleen via Gcc-patches March 19, 2020, 3:07 a.m. | #14
On 3/12/20 6:38 PM, Martin Sebor wrote:
> On 3/12/20 11:03 AM, Martin Sebor wrote:

>> On 3/11/20 3:30 PM, Martin Sebor wrote:

>>> On 3/11/20 2:10 PM, Jason Merrill wrote:

>>>> On 3/11/20 12:57 PM, Martin Sebor wrote:

>>>>> On 3/9/20 6:08 PM, Jason Merrill wrote:

>>>>>> On 3/9/20 5:39 PM, Martin Sebor wrote:

>>>>>>> On 3/9/20 1:40 PM, Jason Merrill wrote:

>>>>>>>> On 3/9/20 12:31 PM, Martin Sebor wrote:

>>>>>>>>> On 2/28/20 1:24 PM, Jason Merrill wrote:

>>>>>>>>>> On 2/28/20 12:45 PM, Martin Sebor wrote:

>>>>>>>>>>> On 2/28/20 9:58 AM, Jason Merrill wrote:

>>>>>>>>>>>> On 2/24/20 6:58 PM, Martin Sebor wrote:

>>>>>>>>>>>>> -Wredundant-tags doesn't consider type declarations that 

>>>>>>>>>>>>> are also

>>>>>>>>>>>>> the first uses of the type, such as in 'void f (struct S);' 

>>>>>>>>>>>>> and

>>>>>>>>>>>>> issues false positives for those.  According to the 

>>>>>>>>>>>>> reported that's

>>>>>>>>>>>>> making it harder to use the warning to clean up LibreOffice.

>>>>>>>>>>>>>

>>>>>>>>>>>>> The attached patch extends -Wredundant-tags to avoid these 

>>>>>>>>>>>>> false

>>>>>>>>>>>>> positives by relying on the same 

>>>>>>>>>>>>> class_decl_loc_t::class2loc mapping

>>>>>>>>>>>>> as -Wmismatched-tags.  The patch also somewhat improves the 

>>>>>>>>>>>>> detection

>>>>>>>>>>>>> of both issues in template declarations (though more work 

>>>>>>>>>>>>> is still

>>>>>>>>>>>>> needed there).

>>>>>>>>>>>>

>>>>>>>>>>>>> +         a new entry for it and return unless it's a 

>>>>>>>>>>>>> declaration

>>>>>>>>>>>>> +         involving a template that may need to be 

>>>>>>>>>>>>> diagnosed by

>>>>>>>>>>>>> +         -Wredundant-tags.  */

>>>>>>>>>>>>>        *rdl = class_decl_loc_t (class_key, false, def_p);

>>>>>>>>>>>>> -      return;

>>>>>>>>>>>>> +      if (TREE_CODE (decl) != TEMPLATE_DECL)

>>>>>>>>>>>>> +        return;

>>>>>>>>>>>>

>>>>>>>>>>>> How can the first appearance of a class template be redundant?

>>>>>>>>>>>

>>>>>>>>>>> I'm not sure I correctly understand the question.  The 

>>>>>>>>>>> comment says

>>>>>>>>>>> "involving a template" (i.e., not one of the first 

>>>>>>>>>>> declaration of

>>>>>>>>>>> a template).  The test case that corresponds to this test is:

>>>>>>>>>>>

>>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>>    struct S7<void> s7v;  // { dg-warning 

>>>>>>>>>>> "\\\[-Wredundant-tags" }

>>>>>>>>>>>

>>>>>>>>>>> where DECL is the TEPLATE_DECL of S7<void>.

>>>>>>>>>>>

>>>>>>>>>>> As I mentioned, more work is still needed to handle templates 

>>>>>>>>>>> right

>>>>>>>>>>> because some redundant tags are still not diagnosed.  For 

>>>>>>>>>>> example:

>>>>>>>>>>>

>>>>>>>>>>>    template <class> struct S7 { };

>>>>>>>>>>>    template <class T>

>>>>>>>>>>>    using U = struct S7<T>;   // missing warning

>>>>>>>>>>

>>>>>>>>>> When we get here for an instance of a template, it doesn't 

>>>>>>>>>> make sense to treat it as a new type.

>>>>>>>>>>

>>>>>>>>>> If decl is a template and type_decl is an instance of that 

>>>>>>>>>> template, do we want to (before the lookup) change type_decl 

>>>>>>>>>> to the template or the corresponding generic TYPE_DECL, which 

>>>>>>>>>> should already be in the table?

>>>>>>>>>

>>>>>>>>> I'm struggling with how to do this.  Given type (a RECORD_TYPE) 

>>>>>>>>> and

>>>>>>>>> type_decl (a TEMPLATE_DECL) representing the use of a template, 

>>>>>>>>> how

>>>>>>>>> do I get the corresponding template (or its explicit or partial

>>>>>>>>> specialization) in the three cases below?

>>>>>>>>>

>>>>>>>>>    1) Instance of the primary:

>>>>>>>>>       template <class> class A;

>>>>>>>>>       struct A<int> a;

>>>>>>>>>

>>>>>>>>>    2) Instance of an explicit specialization:

>>>>>>>>>       template <class> class B;

>>>>>>>>>       template <> struct B<int>;

>>>>>>>>>       class B<int> b;

>>>>>>>>>

>>>>>>>>>    3) Instance of a partial specialization:

>>>>>>>>>       template <class> class C;

>>>>>>>>>       template <class T> struct C<T*>;

>>>>>>>>>       class C<int*> c;

>>>>>>>>>

>>>>>>>>> By trial and (lots of) error I figured out that in both (1) and 

>>>>>>>>> (2),

>>>>>>>>> but not in (3), TYPE_MAIN_DECL (TYPE_TI_TEMPLATE (type)) returns

>>>>>>>>> the template's type_decl.

>>>>>>>>>

>>>>>>>>> Is there some function to call to get it in (3), or even better,

>>>>>>>>> in all three cases?

>>>>>>>>

>>>>>>>> I think you're looking for most_general_template.

>>>>>>>

>>>>>>> I don't think that's quite what I'm looking for.  At least it 

>>>>>>> doesn't

>>>>>>> return the template or its specialization in all three cases above.

>>>>>>

>>>>>> Ah, true, that function stops at specializations.  Oddly, I don't 

>>>>>> think there's currently a similar function that looks through 

>>>>>> them. You could create one that does a simple loop through 

>>>>>> DECL_TI_TEMPLATE like is_specialization_of.

>>>>>

>>>>> Thanks for the tip.  Even with that I'm having trouble with partial

>>>>> specializations.  For example in:

>>>>>

>>>>>    template <class>   struct S;

>>>>>    template <class T> class S<const T>;

>>>>>    extern class  S<const int> s1;

>>>>>    extern struct S<const int> s2;  // expect -Wmismatched-tags

>>>>>

>>>>> how do I find the declaration of the partial specialization when given

>>>>> the type in the extern declaration?  A loop in my find_template_for()

>>>>> function (similar to is_specialization_of) only visits the implicit

>>>>> specialization S<const int> (i.e., its own type) and the primary.

>>>>

>>>> Is that a problem?  The name is from the primary template, so does 

>>>> it matter for this warning whether there's an explicit 

>>>> specialization involved?

>>>

>>> I don't understand the question.  S<const int> is an instance of

>>> the partial specialization.  To diagnose the right mismatch the warning

>>> needs to know how to find the template (i.e., either the primary, or

>>> the explicit or partial specialization) the instance corresponds to and

>>> the class-key it was declared with.  As it is, while GCC does diagnose

>>> the right declaration (that of s2), it does that thanks to a bug:

>>> because it finds and uses the type and class-key used to declare s1.

>>> If we get rid of s1 it doesn't diagnose anything.

>>>

>>> I tried using DECL_TEMPLATE_SPECIALIZATIONS() to get the list of

>>> the partial specializations but it doesn't like any of the arguments

>>> I've given it (it ICEs).

>>

>> With this fixed, here's the algorithm I tried:

>>

>> 1) for a type T of a template instantiation (s1 above), get the primary

>>     P that T was instantiated from using

>>     P = TYPE_MAIN_DECL (CLASSTYPE_PRIMARY_TEMPLATE_TYPE (T)),

>> 2) from P, get the chain of its specializations using

>>     SC = DECL_TEMPLATE_SPECIALIZATIONS (P)

>> 3) for each (partial) specialization S on the SC chain get the chain

>>     of its instantiations IC using DECL_TEMPLATE_INSTANTIATIONS, if

>>     is_specialization_of (T, TREE_VALUE (IC)) is non-zero take

>>     TREE_VALUE (SC) as the declaration of the partial specialization

>>     that the template instanstantiaton T was generated from.

>>

>> Unfortunately, in the example above, DECL_TEMPLATE_INSTANTIATIONS for

>> the partial specialization 'class S<const T>' is null (even after all

>> the declarations have been parsed) so I'm at a dead end.

> 

> After a lot more trial and error I discovered

> most_specialized_partial_spec in pt.c with whose help I have been able

> to get templates to work the way I think they should (at least the cases

> I've tested do).

> 

> Besides fixing the original problem that motivated it, the attached

> patch also corrects how template specializations are handled: the first

> declaration of either a primary template or its specialization (either

> explicit or partial) determines the class-key in subsequent uses of

> the type or its instantiations.  To do this for uses of first-time

> template instantiations such as in the declaration of s1 in the test

> case above, class_decl_loc_t::diag_mismatched_tags looks up the template

> (either the primary or the partial specialization) in the CLASS2LOC map

> and uses it and its class-key as the guide when issuing diagnostics.

> This also means that the first instance of every template needs to

> have a record in the CLASS2LOC map (it also needs it to compare its

> class-key to subsequent redeclarations of the template).

> 

> This has been unexpectedly difficult.  A big part of it is that I've

> never before worked with templates in the front-end so I had to learn

> how they're organized (I'm far from having mastered it).  What's made

> the learning curve especially steep, besides the sparse documentation,

> is the problems hinted at in the paragraph below below.  This whole

> area could really stand to be documented in some sort of a writeup:

> a high-level overview of how templates are put together (i.e., what

> hangs off what in the tree representation) and what APIs to use to

> work with them.

> 

> The revised patch has been tested on x86_64-linux.

> 

> Martin

> 

> 

>> The other recurring problem I'm running into is that many of the C++

>> FE macros (and APIs) don't always return the expected/documented result.

>> I think this is at least in part because until a declaration has been

>> fully parsed not all the bits are in place to make it possible to tell

>> if it's an implicit or explicit specialization, for example (so

>> CLASSTYPE_USE_TEMPLATE (T) is 1 for the first-time declaration of

>> an explicit specialization, for example).  But in view of the problem

>> above I'm not sure that's the only reason.

>>

>>>

>>> Martin

>>>

>>> PS As an aside, both Clang and VC++ have trouble with templates as

>>> well, just slightly different kinds of trouble.   Clang diagnoses

>>> the declaration of the partial specialization while VC++ diagnoses

>>> s1.  In other similar cases like the one below VC++ does the right

>>> thing and Clang is silent.

>>>

>>>    template <class>   struct S { };

>>>    template <class T> class S<const T> { };

>>>

>>>    template <class T> void f (class S<T>);          // VC++ warns

>>>    template <class T> void g (struct S<const T>);   // GCC & VC++ warn

>>>

>>>>

>>>>> Martin

>>>>>

>>>>>>

>>>>>>> In (2) and (3) it won't distinguish between specializations of B or

>>>>>>> C on different types.  In (2), the function returns the same result

>>>>>>> for both:

>>>>>>>

>>>>>>>    template <> struct B<int>;

>>>>>>>    template <> struct B<char>;

>>>>>>>

>>>>>>> In (3), it similarly returns the same result for both of

>>>>>>>

>>>>>>>    template <class T> struct C<T*>;

>>>>>>>    template <class T> struct C<const T>;

>>>>>>>

>>>>>>> even though they are declarations of distinct types.

>>>>>>

>>>>>>

>>>>>> Jason

>>>>>>

>>>>>

>>>>

>>>

>>

> 


> +	 FIXME: Relying on cp_parser_declares_only_class_p to diffeerentiate


"differentiate"

> +	 declarations of a class from its uses doesn't work for type aliases

> +	 (as in using T = class C;).  */


Good point.  Perhaps we could pass flags to 
cp_parser_declares_only_class_p and have it return false if 
CP_PARSER_FLAGS_TYPENAME_OPTIONAL, since that is set for an alias but 
not for a normal type-specifier.

> +   DECL corresponsds.  IS_USE should be set when DECL refers to a class


"corresponds"

> +  for (tree t = decl_type;

> +       t != NULL_TREE;

> +       t = CLASSTYPE_USE_TEMPLATE (t)

> +	 ? TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) : NULL_TREE

> +       )

> +    {

> +      if (same_type_ignoring_top_level_qualifiers_p (t, decl_type))


Looks like on the first iteration, t == decl_type, so this will always 
be true, and this loop has no effect, right?

> +      if (tree spec = most_specialized_partial_spec (ret, tsubst_flags_t ()))


tf_none instead of tsubst_flags_t ().

> +  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)

>      {

> +      /* When TYPE is the use of an implicit specialization of a previously

> +	 declared template set TYPE_DECL to the type of the primary template

> +	 for the specialization and look it up in CLASS2LOC below.  For uses

> +	 of explicit or partial specializations TYPE_DECL already points to

> +	 the declaration of the specialization.  */

> +      type_decl = specialization_of (type_decl);


Here shouldn't is_use be true?

> +  /* Save the current function before changing it below.  */

> +  tree save_func = current_function_decl;


Let's change this (and the restoration at the bottom of the function) to 
a temp_override sentinel.

Jason
Andi Kleen via Gcc-patches March 19, 2020, 11:55 p.m. | #15
On 3/18/20 9:07 PM, Jason Merrill wrote:
> On 3/12/20 6:38 PM, Martin Sebor wrote:

...
>> After a lot more trial and error I discovered

>> most_specialized_partial_spec in pt.c with whose help I have been able

>> to get templates to work the way I think they should (at least the cases

>> I've tested do).

>>

>> Besides fixing the original problem that motivated it, the attached

>> patch also corrects how template specializations are handled: the first

>> declaration of either a primary template or its specialization (either

>> explicit or partial) determines the class-key in subsequent uses of

>> the type or its instantiations.  To do this for uses of first-time

>> template instantiations such as in the declaration of s1 in the test

>> case above, class_decl_loc_t::diag_mismatched_tags looks up the template

>> (either the primary or the partial specialization) in the CLASS2LOC map

>> and uses it and its class-key as the guide when issuing diagnostics.

>> This also means that the first instance of every template needs to

>> have a record in the CLASS2LOC map (it also needs it to compare its

>> class-key to subsequent redeclarations of the template).

>>

>> This has been unexpectedly difficult.  A big part of it is that I've

>> never before worked with templates in the front-end so I had to learn

>> how they're organized (I'm far from having mastered it).  What's made

>> the learning curve especially steep, besides the sparse documentation,

>> is the problems hinted at in the paragraph below below.  This whole

>> area could really stand to be documented in some sort of a writeup:

>> a high-level overview of how templates are put together (i.e., what

>> hangs off what in the tree representation) and what APIs to use to

>> work with them.

>>

>> The revised patch has been tested on x86_64-linux.

>>

...
> 

>> +     FIXME: Relying on cp_parser_declares_only_class_p to diffeerentiate

> 

> "differentiate"

> 

>> +     declarations of a class from its uses doesn't work for type aliases

>> +     (as in using T = class C;).  */

> 

> Good point.  Perhaps we could pass flags to 

> cp_parser_declares_only_class_p and have it return false if 

> CP_PARSER_FLAGS_TYPENAME_OPTIONAL, since that is set for an alias but 

> not for a normal type-specifier.


I wondered if there was a way to identify that we're dealing with
an alias.  CP_PARSER_FLAGS_TYPENAME_OPTIONAL is set not just for
those but also for template declarations (in
cp_parser_single_declaration) but I was able to make it work by
tweaking cp_parser_type_specifier.  It doesn't feel very clean
(it seems like either the bit or all of cp_parser_flags could be
a member of the parser class or some subobject of it) but it does
the job.  Thanks for pointing me in the right direction!

> 

>> +   DECL corresponsds.  IS_USE should be set when DECL refers to a class

> 

> "corresponds"

> 

>> +  for (tree t = decl_type;

>> +       t != NULL_TREE;

>> +       t = CLASSTYPE_USE_TEMPLATE (t)

>> +     ? TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) : NULL_TREE

>> +       )

>> +    {

>> +      if (same_type_ignoring_top_level_qualifiers_p (t, decl_type))

> 

> Looks like on the first iteration, t == decl_type, so this will always 

> be true, and this loop has no effect, right?


Right.

> 

>> +      if (tree spec = most_specialized_partial_spec (ret, 

>> tsubst_flags_t ()))

> 

> tf_none instead of tsubst_flags_t ().

> 

>> +  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)

>>      {

>> +      /* When TYPE is the use of an implicit specialization of a 

>> previously

>> +     declared template set TYPE_DECL to the type of the primary template

>> +     for the specialization and look it up in CLASS2LOC below.  For uses

>> +     of explicit or partial specializations TYPE_DECL already points to

>> +     the declaration of the specialization.  */

>> +      type_decl = specialization_of (type_decl);

> 

> Here shouldn't is_use be true?


If it were set to true here we would find the partial specialization
corresponding to its specialization in the use when what we want is
the latter.  As a result, for the following:

   template <class>   struct S;
   template <class T> struct S<T*>;

   extern class  S<int*> s1;   // expect -Wmismatched-tags
   extern struct S<int*> s2;

we'd end up with a warning for s2 pointing to the instantiation of
s1 as the "guiding declaration:"

z.C:5:15: warning: ‘template<class T> struct S<T*>’ declared with a 
mismatched class-key ‘struct’ [-Wmismatched-tags]
     5 | extern struct S<int*> s2;
       |               ^~~~~~~
z.C:5:15: note: remove the class-key or replace it with ‘class’
z.C:4:15: note: ‘template<class T> struct S<T*>’ first declared as 
‘class’ here
     4 | extern class  S<int*> s1;   // expect -Wmismatched-tags
       |               ^~~~~~~

By setting it to false here we record the type of the instantiation
instead along with the class-key so that we can diagnose the mismatch
between the partial and its use.

> 

>> +  /* Save the current function before changing it below.  */

>> +  tree save_func = current_function_decl;

> 

> Let's change this (and the restoration at the bottom of the function) to 

> a temp_override sentinel.


Attached is a revision with the updates above, rested on x86_64.  To
check the code doesn't ICE on non-trivial use cases I also built stage
1 with both warnings enabled.  It found no ICEs but it sure got a good
workout.  Here's a breakdown of the two warnings (total count, unique
instances, and files they appear in):

   -Wredundant-tags                 727969    21130      839
   -Wmismatched-tags                  2089      536       73

I thought I cleaned up most of the -Wmismatched-tags warnings back
in July so it looks like a lot of them must have crept back in (I
don't think the warning got that much better at finding them in
non-template code).

Martin
PR c++/94078 - bogus and missing -Wmismatched-tags on an instance of a template
PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* cp-tree.h (most_specialized_partial_spec): Declare.
	* parser.c (cp_parser_elaborated_type_specifier): Add argument.
	(cp_parser_declares_only_class_p): Same.
	(cp_parser_postfix_expression): Pass a flag.
	(cp_parser_type_specifier): Clear a flag and pass flags to
	cp_parser_elaborated_type_specifier.
	(cp_parser_declares_only_class_p): Add flags argument.
	(class_decl_loc_t::class_decl_loc_t): Add an argument.
	(class_decl_loc_t::add): Same.
	(class_decl_loc_t::add_or_diag_mismatched_tag): Same.
	(class_decl_loc_t::is_decl): New member function.
	(class_decl_loc_t::class_key_loc_t): Add a new member.
	(specialization_of): New function.
	(cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	Correct handling of template specializations.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.  Use primary template or partial
	specialization as the guide for uses of implicit instantiations.
	* pt.c (most_specialized_partial_spec): Declare extern.

gcc/testsuite/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wmismatched-tags-4.C: New test.
	* g++.dg/warn/Wmismatched-tags-5.C: New test.
	* g++.dg/warn/Wmismatched-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-7.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 757cdd8168a..e21fab367f2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6943,6 +6943,7 @@ extern int comp_template_args			(tree, tree, tree * = NULL,
 extern int template_args_equal                  (tree, tree, bool = false);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
+extern tree most_specialized_partial_spec       (tree, tsubst_flags_t);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, int, tree, tree,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..658cc25fa94 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2192,7 +2192,7 @@ static tree cp_parser_type_name
 static tree cp_parser_nonclass_name
   (cp_parser* parser);
 static tree cp_parser_elaborated_type_specifier
-  (cp_parser *, bool, bool);
+  (cp_parser *, cp_parser_flags, bool, bool);
 static tree cp_parser_enum_specifier
   (cp_parser *);
 static void cp_parser_enumerator_list
@@ -2579,7 +2579,7 @@ static tree cp_parser_sizeof_operand
 static cp_expr cp_parser_trait_expr
   (cp_parser *, enum rid);
 static bool cp_parser_declares_only_class_p
-  (cp_parser *);
+  (cp_parser *, cp_parser_flags = CP_PARSER_FLAGS_NONE);
 static void cp_parser_set_storage_class
   (cp_parser *, cp_decl_specifier_seq *, enum rid, cp_token *);
 static void cp_parser_set_decl_spec_type
@@ -7021,6 +7021,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	   elaborated-type-specifier.  */
         ++parser->prevent_constrained_type_specifiers;
 	type = cp_parser_elaborated_type_specifier (parser,
+						    CP_PARSER_FLAGS_NONE,
 						    /*is_friend=*/false,
 						    /*is_declaration=*/false);
         --parser->prevent_constrained_type_specifiers;
@@ -17682,7 +17683,14 @@ cp_parser_type_specifier (cp_parser* parser,
     elaborated_type_specifier:
       /* We're declaring (not defining) a class or enum.  */
       if (declares_class_or_enum)
-	*declares_class_or_enum = 1;
+	{
+	  *declares_class_or_enum = 1;
+	  /* Clear the flag for classes leaving it set for aliases
+	     for which DECLARES_CLASS_OR_ENUM is null to allow
+	     cp_parser_declares_only_class_p distinguish between
+	     the two.  */
+	  flags &= ~CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
+	}
 
       /* Fall through.  */
     case RID_TYPENAME:
@@ -17690,6 +17698,7 @@ cp_parser_type_specifier (cp_parser* parser,
       type_spec
 	= (cp_parser_elaborated_type_specifier
 	   (parser,
+	    flags,
 	    decl_spec_seq_has_spec_p (decl_specs, ds_friend),
 	    is_declaration));
       if (decl_specs)
@@ -18564,6 +18573,7 @@ cp_parser_nonclass_name (cp_parser* parser)
 
 static tree
 cp_parser_elaborated_type_specifier (cp_parser* parser,
+				     cp_parser_flags flags,
 				     bool is_friend,
 				     bool is_declaration)
 {
@@ -18939,7 +18949,8 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
     {
       /* Diagnose class/struct/union mismatches.  */
       cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
-				 cp_parser_declares_only_class_p (parser));
+				 cp_parser_declares_only_class_p (parser,
+								  flags));
 
       /* Indicate whether this class was declared as a `class' or as a
 	 `struct'.  */
@@ -30138,8 +30149,12 @@ cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword)
 /* If the current declaration has no declarator, return true.  */
 
 static bool
-cp_parser_declares_only_class_p (cp_parser *parser)
+cp_parser_declares_only_class_p (cp_parser *parser, cp_parser_flags flags)
 {
+  if (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL)
+    /* Holds for an alias but not for a normal type-specifier.  */
+    return false;
+
   /* If the next token is a `;' or a `,' then there is no
      declarator.  */
   return (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
@@ -30883,12 +30898,12 @@ class class_decl_loc_t
      DEF_P is true for a class declaration that is a definition.
      CURLOC is the associated location.  */
   class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
-		    location_t curloc = input_location)
+		    bool decl_p, location_t curloc = input_location)
     : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
   {
     locvec.create (4);
     class_key_loc_t ckl (current_function_decl, curloc, class_key,
-			 key_redundant);
+			 key_redundant, def_p || decl_p);
     locvec.quick_push (ckl);
   }
 
@@ -30922,12 +30937,13 @@ class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
-  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool, bool);
 
 private:
 
@@ -30946,6 +30962,11 @@ private:
     return locvec[i].key_redundant;
   }
 
+  bool is_decl (unsigned i) const
+  {
+    return locvec[i].is_decl;
+  }
+
   tag_types class_key (unsigned i) const
   {
     return locvec[i].class_key;
@@ -30955,8 +30976,10 @@ private:
      class-key.  */
   struct class_key_loc_t
   {
-    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
-      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant,
+		     bool decl)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant),
+	is_decl (decl) { }
 
     /* The function the type is mentioned in.  */
     tree func;
@@ -30966,7 +30989,11 @@ private:
     tag_types class_key;
     /* True when the class-key could be omitted at this location
        without an ambiguity with another symbol of the same name.  */
-    bool key_redundant;
+    bool key_redundant: 1;
+    /* True for an entry that corresponds to a dedicated declaration
+       (including a definition) of a class, false for its other uses
+       (such as in declarations of other entities).  */
+    bool is_decl: 1;
   };
   /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
   vec <class_key_loc_t> locvec;
@@ -31019,6 +31046,51 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Returns the template or specialization of one to which the RECORD_TYPE
+   DECL corresponds.  IS_USE should be set when DECL refers to a class
+   that's being used as opposed to being declared.  In that case, if
+   the specialization is the same as itself, returns the most specialized
+   partial specialization of the template to which it corresponds if one
+   exists, or the primary template otherwise.  */
+
+static tree
+specialization_of (tree decl, bool is_use)
+{
+  gcc_assert (TREE_CODE (decl) == TYPE_DECL);
+  tree decl_type = TREE_TYPE (decl);
+  if (!is_use)
+    return decl_type;
+
+  tree ret = decl_type;
+
+  /* RET is the first specialization of the type; since its also
+     a USE of it, it's the first implicit instantiation of it.
+     Determine the template or its partial specialization to which
+     it corresponds.  */
+  if (tree spec = most_specialized_partial_spec (ret, tf_none))
+    if (spec != error_mark_node)
+      ret = TREE_VALUE (spec);
+
+  if (ret == decl_type)
+    ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (decl_type);
+
+  return ret;
+}
+
+
+/* Adds the class TYPE to the collection of class decls and diagnoses
+   redundant tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a (likely, based on syntactic context) declaration of class
+   TYPE and clear for a reference to it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31030,7 +31102,10 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31048,29 +31123,21 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
+  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)
     {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
+      /* When TYPE is the use of an implicit specialization of a previously
+	 declared template set TYPE_DECL to the type of the primary template
+	 for the specialization and look it up in CLASS2LOC below.  For uses
+	 of explicit or partial specializations TYPE_DECL already points to
+	 the declaration of the specialization.
+	 IS_USE is clear so that the type of an implicit instantiation rather
+	 than that of a partial specialization is determined.  */
+      type_decl = specialization_of (type_decl, /* is_use = */false);
+      type_decl = TYPE_MAIN_DECL (type_decl);
     }
 
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it must
+     exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31080,30 +31147,52 @@ class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
-	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
+	  *rdl = class_decl_loc_t (class_key, false, def_p, decl_p);
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled hdeader.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant,
+				   def_p, decl_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31113,7 +31202,7 @@ void
 class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 					      tag_types class_key,
 					      bool redundant,
-					      bool def_p)
+					      bool def_p, bool decl_p)
 {
   /* Reset the CLASS_KEY associated with this type on mismatch.
      This is an optimization that lets the diagnostic code skip
@@ -31128,7 +31217,7 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 
   /* Append a record of this declaration to the vector.  */
   class_key_loc_t ckl (current_function_decl, input_location, class_key,
-		       redundant);
+		       redundant, def_p || decl_p);
   locvec.safe_push (ckl);
 
   if (idxdef == UINT_MAX)
@@ -31156,35 +31245,76 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
-  unsigned ndecls = locvec.length ();
-
-  /* Skip a declaration that consistently uses the same class-key
-     or one with just a solitary declaration (i.e., TYPE_DECL).  */
-  if (def_class_key != none_type || ndecls < 2)
+  if (!warn_mismatched_tags)
     return;
 
-  /* Save the current function before changing it below.  */
-  tree save_func = current_function_decl;
-  /* Set if a class definition for RECLOC has been seen.  */
+  /* Number of usesn of the class.  */
+  unsigned ndecls = locvec.length ();
+
+  /* Set if a definition for the class has been seen.  */
   bool def_p = idxdef < ndecls;
-  unsigned idxguide = def_p ? idxdef : 0;
+
+  /* The class (or template) declaration guiding the decisions about
+     the diagnostic.  For ordinary classes it's the same as THIS.  For
+     uses of instantiations of templates other than their declarations
+     it points to the record for the declaration of the corresponding
+     primary template or partial specialization.  */
+  class_decl_loc_t *cdlguide = this;
+
+  tree type = TREE_TYPE (type_decl);
+  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))
+    {
+      /* For implicit instantiations of a primary template look up
+	 up the primary or partial specialization and use it as
+	 the expected class-key rather than using the class-key of
+	 the first reference to the instantiation.  The primary must
+	 be (and inevitably is) at index zero.  */
+      tree pt = specialization_of (TYPE_MAIN_DECL (type), true);
+      if (TREE_CODE (pt) == TEMPLATE_DECL)
+	pt = TREE_TYPE (pt);
+      pt = TYPE_MAIN_DECL (pt);
+      cdlguide = class2loc.get (pt);
+      gcc_assert (cdlguide != NULL);
+    }
+  else
+    {
+      /* Skip a declaration that consistently uses the same class-key
+	 or one with just a solitary declaration (i.e., TYPE_DECL).  */
+      if (def_class_key != none_type || ndecls < 2)
+	return;
+    }
+
+  /* The index of the declaration whose class-key this declaration
+     is expected to match.  It's either the class-key of the class
+     definintion if one exists or the first declaration otherwise.  */
+  const unsigned idxguide = def_p ? idxdef : 0;
   unsigned idx = 0;
   /* Advance IDX to the first declaration that either is not
      a definition or that doesn't match the first declaration
      if no definition is provided.  */
-  while (class_key (idx) == class_key (idxguide))
+  while (class_key (idx) == cdlguide->class_key (idxguide))
     if (++idx == ndecls)
       return;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
-     if no definition has been provided.  */
-  tag_types xpect_key = class_key (def_p ? idxguide : 0);
-  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
-  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+     if no definition has been provided.
+     For implicit instantiations of a primary template it's
+     the class-key used to declare the primary with.  The primary
+     must be at index zero.  */
+  const tag_types xpect_key
+    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
+  if (cdlguide != this && xpect_key == class_key (idx))
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
   /* Set the function declaration to print in diagnostic context.  */
   current_function_decl = function (idx);
 
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+
   location_t loc = location (idx);
   bool key_redundant_p = key_redundant (idx);
   auto_diagnostic_group d;
@@ -31205,7 +31335,7 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Also point to the first declaration or definition that guided
      the decision to issue the warning above.  */
-  inform (location (idxguide),
+  inform (cdlguide->location (idxguide),
 	  (def_p
 	   ? G_("%qT defined as %qs here")
 	   : G_("%qT first declared as %qs here")),
@@ -31247,25 +31377,29 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
-  /* Save the current function before changing it below.  It should
+  /* Save the current function before changing on return.  It should
      be null at this point.  */
-  tree save_func = current_function_decl;
+  temp_override<tree> cleanup (current_function_decl);
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct/template declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
-  /* Restore the current function.  */
-  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 03a8dfbd37c..0a2c4c86088 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -185,7 +185,7 @@ static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static tree most_specialized_partial_spec (tree, tsubst_flags_t);
+tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@@ -24332,7 +24332,7 @@ most_general_template (tree decl)
    partial specializations matching TARGET, then NULL_TREE is
    returned, indicating that the primary template should be used.  */
 
-static tree
+tree
 most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 {
   tree list = NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
new file mode 100644
index 00000000000..7570264b1d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
@@ -0,0 +1,141 @@
+/* PR c++/94078 - bogus and missing -Wmismatched-tags on an instance
+   of a template
+   Verify that -Wmismatched-tags is issued for redeclarations and
+   instances of the appropriate primary template or specialization.
+  { dg-do compile }
+  { dg-options "-Wmismatched-tags" } */
+
+// Exercise explicit specialization.
+template <class> class S1;
+template <>      struct S1<int>;
+
+template <class> class S1;
+template <class> struct S1;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<char>;
+template <>      struct S1<char>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<int>;       // { dg-warning "\\\[-Wmismatched-tags" }
+template <>      struct S1<int>;
+
+extern        S1<void> s1v;
+extern class  S1<void> s1v;
+extern struct S1<void> s1v;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S1<int> s1i;
+extern class  S1<int> s1i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S1<int> s1i;
+
+extern        S1<char> s1c;
+extern class  S1<char> s1c;
+extern struct S1<char> s1c;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+// Exercise partial specialization.
+template <class>   struct S2;
+template <class T> class S2<const T>;
+
+template <class>   class S2;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S2;
+
+template <class T> class S2<const T>;
+template <class T> struct S2<const T>;// { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S2<int> s2i;
+extern class  S2<int> s2i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S2<int> s2i;
+
+extern        S2<const int> s2ci;
+extern class  S2<const int> s2ci;
+extern struct S2<const int> s2ci;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+template <class>   struct S3;
+template <class T> class S3<T*>;
+template <class T> struct S3<T&>;
+
+template <class>   class S3;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S3;
+
+template <class T> class S3<T*>;
+template <class T> struct S3<T*>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <class T> class S3<T&>;      // { dg-warning "\\\[-Wmismatched-tags" }
+template <class T> struct S3<T&>;
+
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat exactly the same as above.
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat the same as above just with different type.
+extern        S3<long> s3l;
+extern class  S3<long> s3l;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long> s3l;
+
+extern        S3<long*> s3lp;
+extern class  S3<long*> s3lp;
+extern struct S3<long*> s3lp;         // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long&> s3lr;
+extern class  S3<long&> s3lr;         // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long&> s3lr;
+
+// Repeat with the class-keys swapped.
+extern        S3<long> s3l;
+extern struct S3<long> s3l;
+extern class  S3<long> s3l;          // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long*> s3lp;
+extern struct S3<long*> s3lp;        // { dg-warning "\\\[-Wmismatched-tags" }
+extern class  S3<long*> s3lp;
+
+extern        S3<long&> s3lr;
+extern struct S3<long&> s3lr;
+extern class  S3<long&> s3lr;        // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+namespace N
+{
+template <class> struct A;
+
+extern class A<int> ai;               // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct A<int> ai;
+
+typedef class A<int> AI;              // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct A<int> AI;
+
+template <class> struct B;
+template <> class B<int>;
+template <> struct B<char>;
+
+extern class B<int> bi;
+extern struct B<int> bi;              // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern class B<char> bc;              // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct B<char> bc;
+
+typedef class B<char> BC;             // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct B<char> BC;
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
new file mode 100644
index 00000000000..622b9e386ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
@@ -0,0 +1,117 @@
+/* PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef
+   of an implicit class template specialization
+  { dg-do compile }
+  { dg-options "-Wall -Wmismatched-tags" }
+  { dg-require-effective-target c++11 } */
+
+class A;                                // { dg-message "declared as 'class'" }
+typedef        A A0;
+typedef class  A A0;
+typedef struct A A0;                    // { dg-warning "-Wmismatched-tags" }
+
+template <int> struct B;                // { dg-message "declared as 'struct'" }
+typedef        B<0> B0;
+typedef class  B<0> B0;                 // { dg-warning "-Wmismatched-tags" }
+typedef struct B<0> B0;
+
+
+// Exercise member types of templates with non-type arguments.
+template <int> struct CN;               // { dg-message "declared as 'struct'" }
+
+template <int N>
+struct X_CNp1 {
+  typedef CN<N + 1> CNp1;
+};
+
+template <int N>
+struct X_class_CNp1 {
+  typedef class CN<N + 1> CNp1;         // { dg-warning "-Wmismatched-tags" }
+};
+
+template <int N>
+struct X_struct_CNp1 {
+  typedef struct CN<N + 1> CNp1;
+};
+
+
+// Exercise partial specialization of templates with member types.
+template <class> class CT1;
+template <class T> struct CT1<T*> { };
+template <class T> struct CT1<T**> { };
+template <class T> class  CT1<T***> { };
+
+template <class> struct CT2;
+template <class T> struct CT2<T*> {
+  // Expect class-key to match the primary.
+         CT1<T> ct1_0;
+  class  CT1<T> ct1_1;
+  struct CT1<T> ct1_2;                  // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to match the CT1<T*> partial specialization.
+         CT1<T*> ct1p1_0;
+  class  CT1<T*> ct1p1_1;               // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T*> ct1p1_2;
+
+  // Expect class-key to match the CT1<T**> partial specialization.
+         CT1<T**> ct1p2_0;
+  class  CT1<T**> ct1p2_1;              // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T**> ct1p2_2;
+
+  // Expect class-key to match the CT1<T***> partial specialization.
+         CT1<T***> ct1p3_0;
+  class  CT1<T***> ct1p3_1;
+  struct CT1<T***> ct1p3_2;             // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to still match the CT1<T***> partial specialization.
+         CT1<T****> ct1p4_0;
+  class  CT1<T****> ct1p4_1;
+  struct CT1<T****> ct1p4_2;            // { dg-warning "-Wmismatched-tags" }
+};
+
+// Exercise many partial specializations (since the class-key for each
+// must be tracked separately from the others).
+template <class>   class  D;
+template <class T> struct D<T*>;
+template <class T> class  D<T&>;
+template <class T> struct D<const T*>;
+template <class T> class  D<const T&>;
+template <class T> struct D<volatile T*>;
+template <class T> class  D<volatile T&>;
+template <class T> struct D<const volatile T*>;
+template <class T> class  D<const volatile T&>;
+
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+
+typedef class  D<int&>  DIR;
+typedef struct D<int&> DIR;             // { dg-warning "-Wmismatched-tags" }
+typedef class  D<int&>  DIR;
+
+
+typedef struct D<const int*> DCIP;
+typedef class  D<const int*> DCIP;      // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const int*> DCIP;
+
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const int&>  DCIR;
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<volatile int*> DVIP;
+typedef class  D<volatile int*> DVIP;   // { dg-warning "-Wmismatched-tags" }
+typedef struct D<volatile int*> DVIP;
+
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+typedef class  D<volatile int&> DVIR;
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<const volatile int*> DCVIP;
+typedef class  D<const volatile int*> DCVIP;    // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const volatile int*> DCVIP;
+
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const volatile int&> DCVIR;
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
new file mode 100644
index 00000000000..d9d644c4226
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
@@ -0,0 +1,29 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+class A;                      // { dg-message "declared as 'class'" }
+using AA =        A;
+using AA = class  A;
+using AA = struct A;          // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class B;     // { dg-message "declared as 'class'" }
+
+using Bi =        B<int>;
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class C;     // { dg-message "declared as 'class'" }
+
+template <class T> using Cp = C<T*>;
+template <class T> using Cp = class C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
+
+template <class T> using Cp = class  C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@ union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..ce5cb967b79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,91 @@
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 T_S3;           // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 T_S4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <int> struct TS1;
+
+// Verify redeclaration with no definition.
+template <> struct TS1<0>;
+template <> struct TS1<0>;
+
+// Verify definition after a declaration and vice versa.
+template <> struct TS1<1>;
+template <> struct TS1<1> { };
+template <> struct TS1<1>;
+
+// Verify typedefs of an explicit specialization with a definition.
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<1> TS1_1;
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an expplicit specialization.
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<1> ts1_1;
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify typedefs of an implicit specialization without a definition.
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<2> TS1_2;
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an implicit specialization.
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<2> ts1_2;
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+
+
+// Verify partial template specialization.
+template <class> struct TS2;
+template <class T> struct TS2<const T>;
+template <class T> struct TS2<volatile T>;
+
+template <class T>
+struct TS4
+{
+  typedef struct TS2<const T> TS2_CT1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<const T> TS2_CT2;
+
+  typedef struct TS2<T> TS2_T1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<T> TS2_T2;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
new file mode 100644
index 00000000000..872535b9221
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
@@ -0,0 +1,25 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wredundant-tags" } */
+
+class A;
+using AA =        A;
+using AA = class  A;          // { dg-warning "-Wredundant-tags" }
+using AA = struct A;          // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class B;
+
+using Bi =        B<int>;
+using Bi = class  B<int>;     // { dg-warning "-Wredundant-tags" }
+using Bi = struct B<int>;     // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class C;
+
+template <class T>
+using Cp = C<T*>;
+template <class T>
+using Cp = class C<T*>;       // { dg-warning "-Wredundant-tags" }
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wredundant-tags" }
Andi Kleen via Gcc-patches March 20, 2020, 9:53 p.m. | #16
On 3/19/20 7:55 PM, Martin Sebor wrote:
> On 3/18/20 9:07 PM, Jason Merrill wrote:

>> On 3/12/20 6:38 PM, Martin Sebor wrote:

> ...

>>> +     declarations of a class from its uses doesn't work for type 

>>> aliases

>>> +     (as in using T = class C;).  */

>>

>> Good point.  Perhaps we could pass flags to 

>> cp_parser_declares_only_class_p and have it return false if 

>> CP_PARSER_FLAGS_TYPENAME_OPTIONAL, since that is set for an alias but 

>> not for a normal type-specifier.

> 

> I wondered if there was a way to identify that we're dealing with

> an alias.  CP_PARSER_FLAGS_TYPENAME_OPTIONAL is set not just for

> those but also for template declarations (in

> cp_parser_single_declaration) but I was able to make it work by

> tweaking cp_parser_type_specifier.  It doesn't feel very clean

> (it seems like either the bit or all of cp_parser_flags could be

> a member of the parser class or some subobject of it) but it does

> the job.  Thanks for pointing me in the right direction!


Hmm, true, relying on that flag is probably too fragile.  And now that I 
look closer, I see that we already have is_declaration in 
cp_parser_elaborated_type_specifier, we just need to check that before 
cp_parser_declares_only_class_p like we do earlier in the function.

>>> +  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)

>>>      {

>>> +      /* When TYPE is the use of an implicit specialization of a 

>>> previously

>>> +     declared template set TYPE_DECL to the type of the primary 

>>> template

>>> +     for the specialization and look it up in CLASS2LOC below.  For 

>>> uses

>>> +     of explicit or partial specializations TYPE_DECL already points to

>>> +     the declaration of the specialization.  */

>>> +      type_decl = specialization_of (type_decl);

>>

>> Here shouldn't is_use be true?

> 

> If it were set to true here we would find the partial specialization

> corresponding to its specialization in the use when what we want is

> the latter.  As a result, for the following:

> 

>    template <class>   struct S;

>    template <class T> struct S<T*>;

> 

>    extern class  S<int*> s1;   // expect -Wmismatched-tags

>    extern struct S<int*> s2;

> 

> we'd end up with a warning for s2 pointing to the instantiation of

> s1 as the "guiding declaration:"

> 

> z.C:5:15: warning: ‘template<class T> struct S<T*>’ declared with a 

> mismatched class-key ‘struct’ [-Wmismatched-tags]

>      5 | extern struct S<int*> s2;

>        |               ^~~~~~~

> z.C:5:15: note: remove the class-key or replace it with ‘class’

> z.C:4:15: note: ‘template<class T> struct S<T*>’ first declared as 

> ‘class’ here

>      4 | extern class  S<int*> s1;   // expect -Wmismatched-tags

>        |               ^~~~~~~


I found this puzzling and wanted to see why that would be, but I can't 
reproduce it; compiling with -Wmismatched-tags produces only

wa2.C:4:17: warning: ‘S<T*>’ declared with a mismatched class-key 
‘class’ [-Wmismatched-tags]
     4 |   extern class  S<int*> s1;   // expect -Wmismatched-tags
       |                 ^~~~~~~
wa2.C:4:17: note: remove the class-key or replace it with ‘struct’
wa2.C:2:29: note: ‘S<T*>’ first declared as ‘struct’ here
     2 |   template <class T> struct S<T*>;

So the only difference is whether we talk about S<T*> or S<int*>.  I 
agree that the latter is probably better, which is what you get without 
my suggested change.  But since specialization_of does nothing if is_use 
is false, how about removing the call here and removing the is_use 
parameter?

> +  if (tree spec = most_specialized_partial_spec (ret, tf_none))

> +    if (spec != error_mark_node)

> +      ret = TREE_VALUE (spec);


I think you want to take the TREE_TYPE of the template here, so you 
don't need to do it here:

> +      tree pt = specialization_of (TYPE_MAIN_DECL (type), true);

> +      if (TREE_CODE (pt) == TEMPLATE_DECL)

> +	   pt = TREE_TYPE (pt);

> +      pt = TYPE_MAIN_DECL (pt);


And also, since it takes a TYPE_DECL, it would be better to return 
another TYPE_DECL rather than a _TYPE, especially since the only caller 
immediately extracts a TYPE_DECL.

Jason
Andi Kleen via Gcc-patches March 21, 2020, 9:59 p.m. | #17
On 3/20/20 3:53 PM, Jason Merrill wrote:
> On 3/19/20 7:55 PM, Martin Sebor wrote:

>> On 3/18/20 9:07 PM, Jason Merrill wrote:

>>> On 3/12/20 6:38 PM, Martin Sebor wrote:

>> ...

>>>> +     declarations of a class from its uses doesn't work for type 

>>>> aliases

>>>> +     (as in using T = class C;).  */

>>>

>>> Good point.  Perhaps we could pass flags to 

>>> cp_parser_declares_only_class_p and have it return false if 

>>> CP_PARSER_FLAGS_TYPENAME_OPTIONAL, since that is set for an alias but 

>>> not for a normal type-specifier.

>>

>> I wondered if there was a way to identify that we're dealing with

>> an alias.  CP_PARSER_FLAGS_TYPENAME_OPTIONAL is set not just for

>> those but also for template declarations (in

>> cp_parser_single_declaration) but I was able to make it work by

>> tweaking cp_parser_type_specifier.  It doesn't feel very clean

>> (it seems like either the bit or all of cp_parser_flags could be

>> a member of the parser class or some subobject of it) but it does

>> the job.  Thanks for pointing me in the right direction!

> 

> Hmm, true, relying on that flag is probably too fragile.  And now that I 

> look closer, I see that we already have is_declaration in 

> cp_parser_elaborated_type_specifier, we just need to check that before 

> cp_parser_declares_only_class_p like we do earlier in the function.


I changed it to use is_declaration instead.

>>>> +  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)

>>>>      {

>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>> previously

>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>> template

>>>> +     for the specialization and look it up in CLASS2LOC below.  For 

>>>> uses

>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>> points to

>>>> +     the declaration of the specialization.  */

>>>> +      type_decl = specialization_of (type_decl);

>>>

>>> Here shouldn't is_use be true?

>>

>> If it were set to true here we would find the partial specialization

>> corresponding to its specialization in the use when what we want is

>> the latter.  As a result, for the following:

>>

>>    template <class>   struct S;

>>    template <class T> struct S<T*>;

>>

>>    extern class  S<int*> s1;   // expect -Wmismatched-tags

>>    extern struct S<int*> s2;

>>

>> we'd end up with a warning for s2 pointing to the instantiation of

>> s1 as the "guiding declaration:"

>>

>> z.C:5:15: warning: ‘template<class T> struct S<T*>’ declared with a 

>> mismatched class-key ‘struct’ [-Wmismatched-tags]

>>      5 | extern struct S<int*> s2;

>>        |               ^~~~~~~

>> z.C:5:15: note: remove the class-key or replace it with ‘class’

>> z.C:4:15: note: ‘template<class T> struct S<T*>’ first declared as 

>> ‘class’ here

>>      4 | extern class  S<int*> s1;   // expect -Wmismatched-tags

>>        |               ^~~~~~~

> 

> I found this puzzling and wanted to see why that would be, but I can't 

> reproduce it; compiling with -Wmismatched-tags produces only


I'm not sure what you did differently.  With the patch (the last one
or the one in the attachment) we get the expected warning below.

> 

> wa2.C:4:17: warning: ‘S<T*>’ declared with a mismatched class-key 

> ‘class’ [-Wmismatched-tags]

>      4 |   extern class  S<int*> s1;   // expect -Wmismatched-tags

>        |                 ^~~~~~~

> wa2.C:4:17: note: remove the class-key or replace it with ‘struct’

> wa2.C:2:29: note: ‘S<T*>’ first declared as ‘struct’ here

>      2 |   template <class T> struct S<T*>;

> 

> So the only difference is whether we talk about S<T*> or S<int*>.  I 

> agree that the latter is probably better, which is what you get without 

> my suggested change.  But since specialization_of does nothing if is_use 

> is false, how about removing the call here and removing the is_use 

> parameter?


Sure.

> 

>> +  if (tree spec = most_specialized_partial_spec (ret, tf_none))

>> +    if (spec != error_mark_node)

>> +      ret = TREE_VALUE (spec);

> 

> I think you want to take the TREE_TYPE of the template here, so you 

> don't need to do it here:

> 

>> +      tree pt = specialization_of (TYPE_MAIN_DECL (type), true);

>> +      if (TREE_CODE (pt) == TEMPLATE_DECL)

>> +       pt = TREE_TYPE (pt);

>> +      pt = TYPE_MAIN_DECL (pt);

> 

> And also, since it takes a TYPE_DECL, it would be better to return 

> another TYPE_DECL rather than a _TYPE, especially since the only caller 

> immediately extracts a TYPE_DECL.


Okay.  Attached is an revised patch with these changes.

Martin
PR c++/94078 - bogus and missing -Wmismatched-tags on an instance of a template
PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* cp-tree.h (most_specialized_partial_spec): Declare.
	* parser.c (cp_parser_elaborated_type_specifier): Distinguish alias
	from declarations.
	(class_decl_loc_t::class_decl_loc_t): Add an argument.
	(class_decl_loc_t::add): Same.
	(class_decl_loc_t::add_or_diag_mismatched_tag): Same.
	(class_decl_loc_t::is_decl): New member function.
	(class_decl_loc_t::class_key_loc_t): Add a new member.
	(specialization_of): New function.
	(cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	Correct handling of template specializations.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.  Use primary template or partial
	specialization as the guide for uses of implicit instantiations.
	* pt.c (most_specialized_partial_spec): Declare extern.

gcc/testsuite/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wmismatched-tags-4.C: New test.
	* g++.dg/warn/Wmismatched-tags-5.C: New test.
	* g++.dg/warn/Wmismatched-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-7.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0783b3114f2..9c4447202fc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6944,6 +6944,7 @@ extern int comp_template_args			(tree, tree, tree * = NULL,
 extern int template_args_equal                  (tree, tree, bool = false);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
+extern tree most_specialized_partial_spec       (tree, tsubst_flags_t);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, int, tree, tree,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..478a2178852 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18937,7 +18937,10 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
     cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
   else
     {
-      /* Diagnose class/struct/union mismatches.  */
+      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is false
+	 for alias definition.  */
+      bool decl_class = (is_declaration
+			 && cp_parser_declares_only_class_p (parser));
       cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
 				 cp_parser_declares_only_class_p (parser));
 
@@ -30883,12 +30886,12 @@ class class_decl_loc_t
      DEF_P is true for a class declaration that is a definition.
      CURLOC is the associated location.  */
   class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
-		    location_t curloc = input_location)
+		    bool decl_p, location_t curloc = input_location)
     : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
   {
     locvec.create (4);
     class_key_loc_t ckl (current_function_decl, curloc, class_key,
-			 key_redundant);
+			 key_redundant, def_p || decl_p);
     locvec.quick_push (ckl);
   }
 
@@ -30922,12 +30925,13 @@ class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
-  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool, bool);
 
 private:
 
@@ -30946,6 +30950,11 @@ private:
     return locvec[i].key_redundant;
   }
 
+  bool is_decl (unsigned i) const
+  {
+    return locvec[i].is_decl;
+  }
+
   tag_types class_key (unsigned i) const
   {
     return locvec[i].class_key;
@@ -30955,8 +30964,10 @@ private:
      class-key.  */
   struct class_key_loc_t
   {
-    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
-      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant,
+		     bool decl)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant),
+	is_decl (decl) { }
 
     /* The function the type is mentioned in.  */
     tree func;
@@ -30966,7 +30977,11 @@ private:
     tag_types class_key;
     /* True when the class-key could be omitted at this location
        without an ambiguity with another symbol of the same name.  */
-    bool key_redundant;
+    bool key_redundant: 1;
+    /* True for an entry that corresponds to a dedicated declaration
+       (including a definition) of a class, false for its other uses
+       (such as in declarations of other entities).  */
+    bool is_decl: 1;
   };
   /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
   vec <class_key_loc_t> locvec;
@@ -31019,6 +31034,40 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Returns the template or specialization of one to which the RECORD_TYPE
+   TYPE corresponds.  */
+
+static tree
+specialization_of (tree type)
+{
+  tree ret = type;
+
+  /* Determine the template or its partial specialization to which TYPE
+     corresponds.  */
+  if (tree spec = most_specialized_partial_spec (type, tf_none))
+    if (spec != error_mark_node)
+      ret = TREE_TYPE (TREE_VALUE (spec));
+
+  if (ret == type)
+    ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+
+  return TYPE_MAIN_DECL (ret);
+}
+
+
+/* Adds the class TYPE to the collection of class decls and diagnoses
+   redundant tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a (likely, based on syntactic context) declaration of class
+   TYPE and clear for a reference to it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31030,7 +31079,10 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31048,29 +31100,22 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
+  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)
     {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
+      /* When TYPE is the use of an implicit specialization of a previously
+	 declared template set TYPE_DECL to the type of the primary template
+	 for the specialization and look it up in CLASS2LOC below.  For uses
+	 of explicit or partial specializations TYPE_DECL already points to
+	 the declaration of the specialization.
+	 IS_USE is clear so that the type of an implicit instantiation rather
+	 than that of a partial specialization is determined.  */
+      type_decl = TREE_TYPE (type_decl);
+      if (TREE_CODE (type_decl) != TEMPLATE_DECL)
+	type_decl = TYPE_MAIN_DECL (type_decl);
     }
 
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it must
+     exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31080,30 +31125,52 @@ class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
-	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
+	  *rdl = class_decl_loc_t (class_key, false, def_p, decl_p);
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled hdeader.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant,
+				   def_p, decl_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31113,7 +31180,7 @@ void
 class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 					      tag_types class_key,
 					      bool redundant,
-					      bool def_p)
+					      bool def_p, bool decl_p)
 {
   /* Reset the CLASS_KEY associated with this type on mismatch.
      This is an optimization that lets the diagnostic code skip
@@ -31128,7 +31195,7 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 
   /* Append a record of this declaration to the vector.  */
   class_key_loc_t ckl (current_function_decl, input_location, class_key,
-		       redundant);
+		       redundant, def_p || decl_p);
   locvec.safe_push (ckl);
 
   if (idxdef == UINT_MAX)
@@ -31156,35 +31223,73 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
-  unsigned ndecls = locvec.length ();
-
-  /* Skip a declaration that consistently uses the same class-key
-     or one with just a solitary declaration (i.e., TYPE_DECL).  */
-  if (def_class_key != none_type || ndecls < 2)
+  if (!warn_mismatched_tags)
     return;
 
-  /* Save the current function before changing it below.  */
-  tree save_func = current_function_decl;
-  /* Set if a class definition for RECLOC has been seen.  */
+  /* Number of usesn of the class.  */
+  unsigned ndecls = locvec.length ();
+
+  /* Set if a definition for the class has been seen.  */
   bool def_p = idxdef < ndecls;
-  unsigned idxguide = def_p ? idxdef : 0;
+
+  /* The class (or template) declaration guiding the decisions about
+     the diagnostic.  For ordinary classes it's the same as THIS.  For
+     uses of instantiations of templates other than their declarations
+     it points to the record for the declaration of the corresponding
+     primary template or partial specialization.  */
+  class_decl_loc_t *cdlguide = this;
+
+  tree type = TREE_TYPE (type_decl);
+  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))
+    {
+      /* For implicit instantiations of a primary template look up
+	 the primary or partial specialization and use it as
+	 the expected class-key rather than using the class-key of
+	 the first reference to the instantiation.  The primary must
+	 be (and inevitably is) at index zero.  */
+      tree spec = specialization_of (type);
+      cdlguide = class2loc.get (spec);
+      gcc_assert (cdlguide != NULL);
+    }
+  else
+    {
+      /* Skip a declaration that consistently uses the same class-key
+	 or one with just a solitary declaration (i.e., TYPE_DECL).  */
+      if (def_class_key != none_type || ndecls < 2)
+	return;
+    }
+
+  /* The index of the declaration whose class-key this declaration
+     is expected to match.  It's either the class-key of the class
+     definintion if one exists or the first declaration otherwise.  */
+  const unsigned idxguide = def_p ? idxdef : 0;
   unsigned idx = 0;
   /* Advance IDX to the first declaration that either is not
      a definition or that doesn't match the first declaration
      if no definition is provided.  */
-  while (class_key (idx) == class_key (idxguide))
+  while (class_key (idx) == cdlguide->class_key (idxguide))
     if (++idx == ndecls)
       return;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
-     if no definition has been provided.  */
-  tag_types xpect_key = class_key (def_p ? idxguide : 0);
-  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
-  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+     if no definition has been provided.
+     For implicit instantiations of a primary template it's
+     the class-key used to declare the primary with.  The primary
+     must be at index zero.  */
+  const tag_types xpect_key
+    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
+  if (cdlguide != this && xpect_key == class_key (idx))
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
   /* Set the function declaration to print in diagnostic context.  */
   current_function_decl = function (idx);
 
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+
   location_t loc = location (idx);
   bool key_redundant_p = key_redundant (idx);
   auto_diagnostic_group d;
@@ -31205,7 +31310,7 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Also point to the first declaration or definition that guided
      the decision to issue the warning above.  */
-  inform (location (idxguide),
+  inform (cdlguide->location (idxguide),
 	  (def_p
 	   ? G_("%qT defined as %qs here")
 	   : G_("%qT first declared as %qs here")),
@@ -31247,25 +31352,29 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
-  /* Save the current function before changing it below.  It should
+  /* Save the current function before changing on return.  It should
      be null at this point.  */
-  tree save_func = current_function_decl;
+  temp_override<tree> cleanup (current_function_decl);
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct/template declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
-  /* Restore the current function.  */
-  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 03a8dfbd37c..0a2c4c86088 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -185,7 +185,7 @@ static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static tree most_specialized_partial_spec (tree, tsubst_flags_t);
+tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@@ -24332,7 +24332,7 @@ most_general_template (tree decl)
    partial specializations matching TARGET, then NULL_TREE is
    returned, indicating that the primary template should be used.  */
 
-static tree
+tree
 most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 {
   tree list = NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
new file mode 100644
index 00000000000..7570264b1d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
@@ -0,0 +1,141 @@
+/* PR c++/94078 - bogus and missing -Wmismatched-tags on an instance
+   of a template
+   Verify that -Wmismatched-tags is issued for redeclarations and
+   instances of the appropriate primary template or specialization.
+  { dg-do compile }
+  { dg-options "-Wmismatched-tags" } */
+
+// Exercise explicit specialization.
+template <class> class S1;
+template <>      struct S1<int>;
+
+template <class> class S1;
+template <class> struct S1;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<char>;
+template <>      struct S1<char>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<int>;       // { dg-warning "\\\[-Wmismatched-tags" }
+template <>      struct S1<int>;
+
+extern        S1<void> s1v;
+extern class  S1<void> s1v;
+extern struct S1<void> s1v;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S1<int> s1i;
+extern class  S1<int> s1i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S1<int> s1i;
+
+extern        S1<char> s1c;
+extern class  S1<char> s1c;
+extern struct S1<char> s1c;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+// Exercise partial specialization.
+template <class>   struct S2;
+template <class T> class S2<const T>;
+
+template <class>   class S2;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S2;
+
+template <class T> class S2<const T>;
+template <class T> struct S2<const T>;// { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S2<int> s2i;
+extern class  S2<int> s2i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S2<int> s2i;
+
+extern        S2<const int> s2ci;
+extern class  S2<const int> s2ci;
+extern struct S2<const int> s2ci;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+template <class>   struct S3;
+template <class T> class S3<T*>;
+template <class T> struct S3<T&>;
+
+template <class>   class S3;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S3;
+
+template <class T> class S3<T*>;
+template <class T> struct S3<T*>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <class T> class S3<T&>;      // { dg-warning "\\\[-Wmismatched-tags" }
+template <class T> struct S3<T&>;
+
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat exactly the same as above.
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat the same as above just with different type.
+extern        S3<long> s3l;
+extern class  S3<long> s3l;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long> s3l;
+
+extern        S3<long*> s3lp;
+extern class  S3<long*> s3lp;
+extern struct S3<long*> s3lp;         // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long&> s3lr;
+extern class  S3<long&> s3lr;         // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long&> s3lr;
+
+// Repeat with the class-keys swapped.
+extern        S3<long> s3l;
+extern struct S3<long> s3l;
+extern class  S3<long> s3l;          // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long*> s3lp;
+extern struct S3<long*> s3lp;        // { dg-warning "\\\[-Wmismatched-tags" }
+extern class  S3<long*> s3lp;
+
+extern        S3<long&> s3lr;
+extern struct S3<long&> s3lr;
+extern class  S3<long&> s3lr;        // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+namespace N
+{
+template <class> struct A;
+
+extern class A<int> ai;               // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct A<int> ai;
+
+typedef class A<int> AI;              // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct A<int> AI;
+
+template <class> struct B;
+template <> class B<int>;
+template <> struct B<char>;
+
+extern class B<int> bi;
+extern struct B<int> bi;              // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern class B<char> bc;              // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct B<char> bc;
+
+typedef class B<char> BC;             // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct B<char> BC;
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
new file mode 100644
index 00000000000..622b9e386ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
@@ -0,0 +1,117 @@
+/* PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef
+   of an implicit class template specialization
+  { dg-do compile }
+  { dg-options "-Wall -Wmismatched-tags" }
+  { dg-require-effective-target c++11 } */
+
+class A;                                // { dg-message "declared as 'class'" }
+typedef        A A0;
+typedef class  A A0;
+typedef struct A A0;                    // { dg-warning "-Wmismatched-tags" }
+
+template <int> struct B;                // { dg-message "declared as 'struct'" }
+typedef        B<0> B0;
+typedef class  B<0> B0;                 // { dg-warning "-Wmismatched-tags" }
+typedef struct B<0> B0;
+
+
+// Exercise member types of templates with non-type arguments.
+template <int> struct CN;               // { dg-message "declared as 'struct'" }
+
+template <int N>
+struct X_CNp1 {
+  typedef CN<N + 1> CNp1;
+};
+
+template <int N>
+struct X_class_CNp1 {
+  typedef class CN<N + 1> CNp1;         // { dg-warning "-Wmismatched-tags" }
+};
+
+template <int N>
+struct X_struct_CNp1 {
+  typedef struct CN<N + 1> CNp1;
+};
+
+
+// Exercise partial specialization of templates with member types.
+template <class> class CT1;
+template <class T> struct CT1<T*> { };
+template <class T> struct CT1<T**> { };
+template <class T> class  CT1<T***> { };
+
+template <class> struct CT2;
+template <class T> struct CT2<T*> {
+  // Expect class-key to match the primary.
+         CT1<T> ct1_0;
+  class  CT1<T> ct1_1;
+  struct CT1<T> ct1_2;                  // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to match the CT1<T*> partial specialization.
+         CT1<T*> ct1p1_0;
+  class  CT1<T*> ct1p1_1;               // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T*> ct1p1_2;
+
+  // Expect class-key to match the CT1<T**> partial specialization.
+         CT1<T**> ct1p2_0;
+  class  CT1<T**> ct1p2_1;              // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T**> ct1p2_2;
+
+  // Expect class-key to match the CT1<T***> partial specialization.
+         CT1<T***> ct1p3_0;
+  class  CT1<T***> ct1p3_1;
+  struct CT1<T***> ct1p3_2;             // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to still match the CT1<T***> partial specialization.
+         CT1<T****> ct1p4_0;
+  class  CT1<T****> ct1p4_1;
+  struct CT1<T****> ct1p4_2;            // { dg-warning "-Wmismatched-tags" }
+};
+
+// Exercise many partial specializations (since the class-key for each
+// must be tracked separately from the others).
+template <class>   class  D;
+template <class T> struct D<T*>;
+template <class T> class  D<T&>;
+template <class T> struct D<const T*>;
+template <class T> class  D<const T&>;
+template <class T> struct D<volatile T*>;
+template <class T> class  D<volatile T&>;
+template <class T> struct D<const volatile T*>;
+template <class T> class  D<const volatile T&>;
+
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+
+typedef class  D<int&>  DIR;
+typedef struct D<int&> DIR;             // { dg-warning "-Wmismatched-tags" }
+typedef class  D<int&>  DIR;
+
+
+typedef struct D<const int*> DCIP;
+typedef class  D<const int*> DCIP;      // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const int*> DCIP;
+
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const int&>  DCIR;
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<volatile int*> DVIP;
+typedef class  D<volatile int*> DVIP;   // { dg-warning "-Wmismatched-tags" }
+typedef struct D<volatile int*> DVIP;
+
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+typedef class  D<volatile int&> DVIR;
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<const volatile int*> DCVIP;
+typedef class  D<const volatile int*> DCVIP;    // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const volatile int*> DCVIP;
+
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const volatile int&> DCVIR;
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
new file mode 100644
index 00000000000..d9d644c4226
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
@@ -0,0 +1,29 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+class A;                      // { dg-message "declared as 'class'" }
+using AA =        A;
+using AA = class  A;
+using AA = struct A;          // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class B;     // { dg-message "declared as 'class'" }
+
+using Bi =        B<int>;
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class C;     // { dg-message "declared as 'class'" }
+
+template <class T> using Cp = C<T*>;
+template <class T> using Cp = class C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
+
+template <class T> using Cp = class  C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@ union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..ce5cb967b79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,91 @@
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 T_S3;           // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 T_S4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <int> struct TS1;
+
+// Verify redeclaration with no definition.
+template <> struct TS1<0>;
+template <> struct TS1<0>;
+
+// Verify definition after a declaration and vice versa.
+template <> struct TS1<1>;
+template <> struct TS1<1> { };
+template <> struct TS1<1>;
+
+// Verify typedefs of an explicit specialization with a definition.
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<1> TS1_1;
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an expplicit specialization.
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<1> ts1_1;
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify typedefs of an implicit specialization without a definition.
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<2> TS1_2;
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an implicit specialization.
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<2> ts1_2;
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+
+
+// Verify partial template specialization.
+template <class> struct TS2;
+template <class T> struct TS2<const T>;
+template <class T> struct TS2<volatile T>;
+
+template <class T>
+struct TS4
+{
+  typedef struct TS2<const T> TS2_CT1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<const T> TS2_CT2;
+
+  typedef struct TS2<T> TS2_T1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<T> TS2_T2;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
new file mode 100644
index 00000000000..872535b9221
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
@@ -0,0 +1,25 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wredundant-tags" } */
+
+class A;
+using AA =        A;
+using AA = class  A;          // { dg-warning "-Wredundant-tags" }
+using AA = struct A;          // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class B;
+
+using Bi =        B<int>;
+using Bi = class  B<int>;     // { dg-warning "-Wredundant-tags" }
+using Bi = struct B<int>;     // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class C;
+
+template <class T>
+using Cp = C<T*>;
+template <class T>
+using Cp = class C<T*>;       // { dg-warning "-Wredundant-tags" }
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wredundant-tags" }
Andi Kleen via Gcc-patches March 23, 2020, 2:49 p.m. | #18
On 3/21/20 5:59 PM, Martin Sebor wrote:
> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is false

> +	 for alias definition.  */

> +      bool decl_class = (is_declaration

> +			 && cp_parser_declares_only_class_p (parser));

>         cp_parser_check_class_key (parser, key_loc, tag_type, type, false,

>   				 cp_parser_declares_only_class_p (parser));


Don't you need to use the new variable?

Don't your testcases exercise this?

> +      /* When TYPE is the use of an implicit specialization of a previously

> +	 declared template set TYPE_DECL to the type of the primary template

> +	 for the specialization and look it up in CLASS2LOC below.  For uses

> +	 of explicit or partial specializations TYPE_DECL already points to

> +	 the declaration of the specialization.

> +	 IS_USE is clear so that the type of an implicit instantiation rather

> +	 than that of a partial specialization is determined.  */

> +      type_decl = TREE_TYPE (type_decl);

> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

> +	type_decl = TYPE_MAIN_DECL (type_decl);


The comment is no longer relevant to the code.  The remaining code also 
seems like it would have no effect; we already know type_decl is 
TYPE_MAIN_DECL (type).

Jason
Andi Kleen via Gcc-patches March 23, 2020, 4:50 p.m. | #19
On 3/23/20 8:49 AM, Jason Merrill wrote:
> On 3/21/20 5:59 PM, Martin Sebor wrote:

>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is 

>> false

>> +     for alias definition.  */

>> +      bool decl_class = (is_declaration

>> +             && cp_parser_declares_only_class_p (parser));

>>         cp_parser_check_class_key (parser, key_loc, tag_type, type, 

>> false,

>>                    cp_parser_declares_only_class_p (parser));

> 

> Don't you need to use the new variable?

> 

> Don't your testcases exercise this?


Of course they do.  This was a leftover from an experiment after I put
the initial updated patch together.  On final review I decided to adjust
some comments and forgot to restore the original use of the variable.

>> +      /* When TYPE is the use of an implicit specialization of a 

>> previously

>> +     declared template set TYPE_DECL to the type of the primary template

>> +     for the specialization and look it up in CLASS2LOC below.  For uses

>> +     of explicit or partial specializations TYPE_DECL already points to

>> +     the declaration of the specialization.

>> +     IS_USE is clear so that the type of an implicit instantiation 

>> rather

>> +     than that of a partial specialization is determined.  */

>> +      type_decl = TREE_TYPE (type_decl);

>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>> +    type_decl = TYPE_MAIN_DECL (type_decl);

> 

> The comment is no longer relevant to the code.  The remaining code also 

> seems like it would have no effect; we already know type_decl is 

> TYPE_MAIN_DECL (type).


I removed the block of code.

Martin

PS I would have preferred to resolve just the reported problem in this
patch and deal with the template specializations more fully (and with
aliases) in a followup.  As it is, it has grown bigger and more complex
than I'm comfortable with, especially with the template specializations,
harder for me to follow, and obviously a lot more time-consuming not
just to put together but also to review.  Although this revision handles
many more template specialization cases correctly, there still are other
(arguably corner) cases that it doesn't.  I suspect getting those right
might even require a design change, which I see as out of scope at this
time (not to mention my ability).
PR c++/94078 - bogus and missing -Wmismatched-tags on an instance of a template
PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* cp-tree.h (most_specialized_partial_spec): Declare.
	* parser.c (cp_parser_elaborated_type_specifier): Distinguish alias
	from declarations.
	(class_decl_loc_t::class_decl_loc_t): Add an argument.
	(class_decl_loc_t::add): Same.
	(class_decl_loc_t::add_or_diag_mismatched_tag): Same.
	(class_decl_loc_t::is_decl): New member function.
	(class_decl_loc_t::class_key_loc_t): Add a new member.
	(specialization_of): New function.
	(cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	Correct handling of template specializations.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.  Use primary template or partial
	specialization as the guide for uses of implicit instantiations.
	* pt.c (most_specialized_partial_spec): Declare extern.

gcc/testsuite/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wmismatched-tags-4.C: New test.
	* g++.dg/warn/Wmismatched-tags-5.C: New test.
	* g++.dg/warn/Wmismatched-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-7.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0783b3114f2..9c4447202fc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6944,6 +6944,7 @@ extern int comp_template_args			(tree, tree, tree * = NULL,
 extern int template_args_equal                  (tree, tree, bool = false);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
+extern tree most_specialized_partial_spec       (tree, tsubst_flags_t);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, int, tree, tree,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..8851940710e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18937,9 +18937,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
     cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
   else
     {
-      /* Diagnose class/struct/union mismatches.  */
+      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is false
+	 for alias definition.  */
+      bool decl_class = (is_declaration
+			 && cp_parser_declares_only_class_p (parser));
       cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
-				 cp_parser_declares_only_class_p (parser));
+				 decl_class);
 
       /* Indicate whether this class was declared as a `class' or as a
 	 `struct'.  */
@@ -30883,12 +30886,12 @@ class class_decl_loc_t
      DEF_P is true for a class declaration that is a definition.
      CURLOC is the associated location.  */
   class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p,
-		    location_t curloc = input_location)
+		    bool decl_p, location_t curloc = input_location)
     : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key)
   {
     locvec.create (4);
     class_key_loc_t ckl (current_function_decl, curloc, class_key,
-			 key_redundant);
+			 key_redundant, def_p || decl_p);
     locvec.quick_push (ckl);
   }
 
@@ -30922,12 +30925,13 @@ class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
-  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool);
+  void add_or_diag_mismatched_tag (tree, tag_types, bool, bool, bool);
 
 private:
 
@@ -30946,6 +30950,11 @@ private:
     return locvec[i].key_redundant;
   }
 
+  bool is_decl (unsigned i) const
+  {
+    return locvec[i].is_decl;
+  }
+
   tag_types class_key (unsigned i) const
   {
     return locvec[i].class_key;
@@ -30955,8 +30964,10 @@ private:
      class-key.  */
   struct class_key_loc_t
   {
-    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
-      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+    class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant,
+		     bool decl)
+      : func (func), loc (loc), class_key (key), key_redundant (redundant),
+	is_decl (decl) { }
 
     /* The function the type is mentioned in.  */
     tree func;
@@ -30966,7 +30977,11 @@ private:
     tag_types class_key;
     /* True when the class-key could be omitted at this location
        without an ambiguity with another symbol of the same name.  */
-    bool key_redundant;
+    bool key_redundant: 1;
+    /* True for an entry that corresponds to a dedicated declaration
+       (including a definition) of a class, false for its other uses
+       (such as in declarations of other entities).  */
+    bool is_decl: 1;
   };
   /* Avoid using auto_vec here since it's not safe to copy due to pr90904.  */
   vec <class_key_loc_t> locvec;
@@ -31019,6 +31034,40 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Returns the template or specialization of one to which the RECORD_TYPE
+   TYPE corresponds.  */
+
+static tree
+specialization_of (tree type)
+{
+  tree ret = type;
+
+  /* Determine the template or its partial specialization to which TYPE
+     corresponds.  */
+  if (tree spec = most_specialized_partial_spec (type, tf_none))
+    if (spec != error_mark_node)
+      ret = TREE_TYPE (TREE_VALUE (spec));
+
+  if (ret == type)
+    ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+
+  return TYPE_MAIN_DECL (ret);
+}
+
+
+/* Adds the class TYPE to the collection of class decls and diagnoses
+   redundant tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a (likely, based on syntactic context) declaration of class
+   TYPE and clear for a reference to it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31030,7 +31079,10 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31048,29 +31100,8 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
-    {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
-    }
-
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it must
+     exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31080,30 +31111,52 @@ class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
-	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
+	  *rdl = class_decl_loc_t (class_key, false, def_p, decl_p);
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled hdeader.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant,
+				   def_p, decl_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31113,7 +31166,7 @@ void
 class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 					      tag_types class_key,
 					      bool redundant,
-					      bool def_p)
+					      bool def_p, bool decl_p)
 {
   /* Reset the CLASS_KEY associated with this type on mismatch.
      This is an optimization that lets the diagnostic code skip
@@ -31128,7 +31181,7 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 
   /* Append a record of this declaration to the vector.  */
   class_key_loc_t ckl (current_function_decl, input_location, class_key,
-		       redundant);
+		       redundant, def_p || decl_p);
   locvec.safe_push (ckl);
 
   if (idxdef == UINT_MAX)
@@ -31156,35 +31209,73 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
-  unsigned ndecls = locvec.length ();
-
-  /* Skip a declaration that consistently uses the same class-key
-     or one with just a solitary declaration (i.e., TYPE_DECL).  */
-  if (def_class_key != none_type || ndecls < 2)
+  if (!warn_mismatched_tags)
     return;
 
-  /* Save the current function before changing it below.  */
-  tree save_func = current_function_decl;
-  /* Set if a class definition for RECLOC has been seen.  */
+  /* Number of usesn of the class.  */
+  unsigned ndecls = locvec.length ();
+
+  /* Set if a definition for the class has been seen.  */
   bool def_p = idxdef < ndecls;
-  unsigned idxguide = def_p ? idxdef : 0;
+
+  /* The class (or template) declaration guiding the decisions about
+     the diagnostic.  For ordinary classes it's the same as THIS.  For
+     uses of instantiations of templates other than their declarations
+     it points to the record for the declaration of the corresponding
+     primary template or partial specialization.  */
+  class_decl_loc_t *cdlguide = this;
+
+  tree type = TREE_TYPE (type_decl);
+  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))
+    {
+      /* For implicit instantiations of a primary template look up
+	 the primary or partial specialization and use it as
+	 the expected class-key rather than using the class-key of
+	 the first reference to the instantiation.  The primary must
+	 be (and inevitably is) at index zero.  */
+      tree spec = specialization_of (type);
+      cdlguide = class2loc.get (spec);
+      gcc_assert (cdlguide != NULL);
+    }
+  else
+    {
+      /* Skip a declaration that consistently uses the same class-key
+	 or one with just a solitary declaration (i.e., TYPE_DECL).  */
+      if (def_class_key != none_type || ndecls < 2)
+	return;
+    }
+
+  /* The index of the declaration whose class-key this declaration
+     is expected to match.  It's either the class-key of the class
+     definintion if one exists or the first declaration otherwise.  */
+  const unsigned idxguide = def_p ? idxdef : 0;
   unsigned idx = 0;
   /* Advance IDX to the first declaration that either is not
      a definition or that doesn't match the first declaration
      if no definition is provided.  */
-  while (class_key (idx) == class_key (idxguide))
+  while (class_key (idx) == cdlguide->class_key (idxguide))
     if (++idx == ndecls)
       return;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
-     if no definition has been provided.  */
-  tag_types xpect_key = class_key (def_p ? idxguide : 0);
-  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
-  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+     if no definition has been provided.
+     For implicit instantiations of a primary template it's
+     the class-key used to declare the primary with.  The primary
+     must be at index zero.  */
+  const tag_types xpect_key
+    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
+  if (cdlguide != this && xpect_key == class_key (idx))
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
   /* Set the function declaration to print in diagnostic context.  */
   current_function_decl = function (idx);
 
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+
   location_t loc = location (idx);
   bool key_redundant_p = key_redundant (idx);
   auto_diagnostic_group d;
@@ -31205,7 +31296,7 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Also point to the first declaration or definition that guided
      the decision to issue the warning above.  */
-  inform (location (idxguide),
+  inform (cdlguide->location (idxguide),
 	  (def_p
 	   ? G_("%qT defined as %qs here")
 	   : G_("%qT first declared as %qs here")),
@@ -31247,25 +31338,29 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
-  /* Save the current function before changing it below.  It should
+  /* Save the current function before changing on return.  It should
      be null at this point.  */
-  tree save_func = current_function_decl;
+  temp_override<tree> cleanup (current_function_decl);
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct/template declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
-  /* Restore the current function.  */
-  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 03a8dfbd37c..0a2c4c86088 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -185,7 +185,7 @@ static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static tree most_specialized_partial_spec (tree, tsubst_flags_t);
+tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@@ -24332,7 +24332,7 @@ most_general_template (tree decl)
    partial specializations matching TARGET, then NULL_TREE is
    returned, indicating that the primary template should be used.  */
 
-static tree
+tree
 most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 {
   tree list = NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
new file mode 100644
index 00000000000..7570264b1d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
@@ -0,0 +1,141 @@
+/* PR c++/94078 - bogus and missing -Wmismatched-tags on an instance
+   of a template
+   Verify that -Wmismatched-tags is issued for redeclarations and
+   instances of the appropriate primary template or specialization.
+  { dg-do compile }
+  { dg-options "-Wmismatched-tags" } */
+
+// Exercise explicit specialization.
+template <class> class S1;
+template <>      struct S1<int>;
+
+template <class> class S1;
+template <class> struct S1;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<char>;
+template <>      struct S1<char>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<int>;       // { dg-warning "\\\[-Wmismatched-tags" }
+template <>      struct S1<int>;
+
+extern        S1<void> s1v;
+extern class  S1<void> s1v;
+extern struct S1<void> s1v;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S1<int> s1i;
+extern class  S1<int> s1i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S1<int> s1i;
+
+extern        S1<char> s1c;
+extern class  S1<char> s1c;
+extern struct S1<char> s1c;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+// Exercise partial specialization.
+template <class>   struct S2;
+template <class T> class S2<const T>;
+
+template <class>   class S2;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S2;
+
+template <class T> class S2<const T>;
+template <class T> struct S2<const T>;// { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S2<int> s2i;
+extern class  S2<int> s2i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S2<int> s2i;
+
+extern        S2<const int> s2ci;
+extern class  S2<const int> s2ci;
+extern struct S2<const int> s2ci;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+template <class>   struct S3;
+template <class T> class S3<T*>;
+template <class T> struct S3<T&>;
+
+template <class>   class S3;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S3;
+
+template <class T> class S3<T*>;
+template <class T> struct S3<T*>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <class T> class S3<T&>;      // { dg-warning "\\\[-Wmismatched-tags" }
+template <class T> struct S3<T&>;
+
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat exactly the same as above.
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat the same as above just with different type.
+extern        S3<long> s3l;
+extern class  S3<long> s3l;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long> s3l;
+
+extern        S3<long*> s3lp;
+extern class  S3<long*> s3lp;
+extern struct S3<long*> s3lp;         // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long&> s3lr;
+extern class  S3<long&> s3lr;         // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long&> s3lr;
+
+// Repeat with the class-keys swapped.
+extern        S3<long> s3l;
+extern struct S3<long> s3l;
+extern class  S3<long> s3l;          // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long*> s3lp;
+extern struct S3<long*> s3lp;        // { dg-warning "\\\[-Wmismatched-tags" }
+extern class  S3<long*> s3lp;
+
+extern        S3<long&> s3lr;
+extern struct S3<long&> s3lr;
+extern class  S3<long&> s3lr;        // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+namespace N
+{
+template <class> struct A;
+
+extern class A<int> ai;               // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct A<int> ai;
+
+typedef class A<int> AI;              // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct A<int> AI;
+
+template <class> struct B;
+template <> class B<int>;
+template <> struct B<char>;
+
+extern class B<int> bi;
+extern struct B<int> bi;              // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern class B<char> bc;              // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct B<char> bc;
+
+typedef class B<char> BC;             // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct B<char> BC;
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
new file mode 100644
index 00000000000..622b9e386ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
@@ -0,0 +1,117 @@
+/* PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef
+   of an implicit class template specialization
+  { dg-do compile }
+  { dg-options "-Wall -Wmismatched-tags" }
+  { dg-require-effective-target c++11 } */
+
+class A;                                // { dg-message "declared as 'class'" }
+typedef        A A0;
+typedef class  A A0;
+typedef struct A A0;                    // { dg-warning "-Wmismatched-tags" }
+
+template <int> struct B;                // { dg-message "declared as 'struct'" }
+typedef        B<0> B0;
+typedef class  B<0> B0;                 // { dg-warning "-Wmismatched-tags" }
+typedef struct B<0> B0;
+
+
+// Exercise member types of templates with non-type arguments.
+template <int> struct CN;               // { dg-message "declared as 'struct'" }
+
+template <int N>
+struct X_CNp1 {
+  typedef CN<N + 1> CNp1;
+};
+
+template <int N>
+struct X_class_CNp1 {
+  typedef class CN<N + 1> CNp1;         // { dg-warning "-Wmismatched-tags" }
+};
+
+template <int N>
+struct X_struct_CNp1 {
+  typedef struct CN<N + 1> CNp1;
+};
+
+
+// Exercise partial specialization of templates with member types.
+template <class> class CT1;
+template <class T> struct CT1<T*> { };
+template <class T> struct CT1<T**> { };
+template <class T> class  CT1<T***> { };
+
+template <class> struct CT2;
+template <class T> struct CT2<T*> {
+  // Expect class-key to match the primary.
+         CT1<T> ct1_0;
+  class  CT1<T> ct1_1;
+  struct CT1<T> ct1_2;                  // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to match the CT1<T*> partial specialization.
+         CT1<T*> ct1p1_0;
+  class  CT1<T*> ct1p1_1;               // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T*> ct1p1_2;
+
+  // Expect class-key to match the CT1<T**> partial specialization.
+         CT1<T**> ct1p2_0;
+  class  CT1<T**> ct1p2_1;              // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T**> ct1p2_2;
+
+  // Expect class-key to match the CT1<T***> partial specialization.
+         CT1<T***> ct1p3_0;
+  class  CT1<T***> ct1p3_1;
+  struct CT1<T***> ct1p3_2;             // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to still match the CT1<T***> partial specialization.
+         CT1<T****> ct1p4_0;
+  class  CT1<T****> ct1p4_1;
+  struct CT1<T****> ct1p4_2;            // { dg-warning "-Wmismatched-tags" }
+};
+
+// Exercise many partial specializations (since the class-key for each
+// must be tracked separately from the others).
+template <class>   class  D;
+template <class T> struct D<T*>;
+template <class T> class  D<T&>;
+template <class T> struct D<const T*>;
+template <class T> class  D<const T&>;
+template <class T> struct D<volatile T*>;
+template <class T> class  D<volatile T&>;
+template <class T> struct D<const volatile T*>;
+template <class T> class  D<const volatile T&>;
+
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+
+typedef class  D<int&>  DIR;
+typedef struct D<int&> DIR;             // { dg-warning "-Wmismatched-tags" }
+typedef class  D<int&>  DIR;
+
+
+typedef struct D<const int*> DCIP;
+typedef class  D<const int*> DCIP;      // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const int*> DCIP;
+
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const int&>  DCIR;
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<volatile int*> DVIP;
+typedef class  D<volatile int*> DVIP;   // { dg-warning "-Wmismatched-tags" }
+typedef struct D<volatile int*> DVIP;
+
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+typedef class  D<volatile int&> DVIR;
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<const volatile int*> DCVIP;
+typedef class  D<const volatile int*> DCVIP;    // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const volatile int*> DCVIP;
+
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const volatile int&> DCVIR;
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
new file mode 100644
index 00000000000..d9d644c4226
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
@@ -0,0 +1,29 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+class A;                      // { dg-message "declared as 'class'" }
+using AA =        A;
+using AA = class  A;
+using AA = struct A;          // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class B;     // { dg-message "declared as 'class'" }
+
+using Bi =        B<int>;
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class C;     // { dg-message "declared as 'class'" }
+
+template <class T> using Cp = C<T*>;
+template <class T> using Cp = class C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
+
+template <class T> using Cp = class  C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@ union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..ce5cb967b79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,91 @@
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 T_S3;           // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 T_S4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <int> struct TS1;
+
+// Verify redeclaration with no definition.
+template <> struct TS1<0>;
+template <> struct TS1<0>;
+
+// Verify definition after a declaration and vice versa.
+template <> struct TS1<1>;
+template <> struct TS1<1> { };
+template <> struct TS1<1>;
+
+// Verify typedefs of an explicit specialization with a definition.
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<1> TS1_1;
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an expplicit specialization.
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<1> ts1_1;
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify typedefs of an implicit specialization without a definition.
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<2> TS1_2;
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an implicit specialization.
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<2> ts1_2;
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+
+
+// Verify partial template specialization.
+template <class> struct TS2;
+template <class T> struct TS2<const T>;
+template <class T> struct TS2<volatile T>;
+
+template <class T>
+struct TS4
+{
+  typedef struct TS2<const T> TS2_CT1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<const T> TS2_CT2;
+
+  typedef struct TS2<T> TS2_T1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<T> TS2_T2;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
new file mode 100644
index 00000000000..872535b9221
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
@@ -0,0 +1,25 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wredundant-tags" } */
+
+class A;
+using AA =        A;
+using AA = class  A;          // { dg-warning "-Wredundant-tags" }
+using AA = struct A;          // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class B;
+
+using Bi =        B<int>;
+using Bi = class  B<int>;     // { dg-warning "-Wredundant-tags" }
+using Bi = struct B<int>;     // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class C;
+
+template <class T>
+using Cp = C<T*>;
+template <class T>
+using Cp = class C<T*>;       // { dg-warning "-Wredundant-tags" }
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wredundant-tags" }
Andi Kleen via Gcc-patches March 25, 2020, 8:54 p.m. | #20
Ping: Jason, is the latest patch acceptable or are there any other
changes you want me to make?

   https://gcc.gnu.org/pipermail/gcc-patches/2020-March/542538.html

On 3/21/20 3:59 PM, Martin Sebor wrote:
> On 3/20/20 3:53 PM, Jason Merrill wrote:

>> On 3/19/20 7:55 PM, Martin Sebor wrote:

>>> On 3/18/20 9:07 PM, Jason Merrill wrote:

>>>> On 3/12/20 6:38 PM, Martin Sebor wrote:

>>> ...

>>>>> +     declarations of a class from its uses doesn't work for type 

>>>>> aliases

>>>>> +     (as in using T = class C;).  */

>>>>

>>>> Good point.  Perhaps we could pass flags to 

>>>> cp_parser_declares_only_class_p and have it return false if 

>>>> CP_PARSER_FLAGS_TYPENAME_OPTIONAL, since that is set for an alias 

>>>> but not for a normal type-specifier.

>>>

>>> I wondered if there was a way to identify that we're dealing with

>>> an alias.  CP_PARSER_FLAGS_TYPENAME_OPTIONAL is set not just for

>>> those but also for template declarations (in

>>> cp_parser_single_declaration) but I was able to make it work by

>>> tweaking cp_parser_type_specifier.  It doesn't feel very clean

>>> (it seems like either the bit or all of cp_parser_flags could be

>>> a member of the parser class or some subobject of it) but it does

>>> the job.  Thanks for pointing me in the right direction!

>>

>> Hmm, true, relying on that flag is probably too fragile.  And now that 

>> I look closer, I see that we already have is_declaration in 

>> cp_parser_elaborated_type_specifier, we just need to check that before 

>> cp_parser_declares_only_class_p like we do earlier in the function.

> 

> I changed it to use is_declaration instead.

> 

>>>>> +  if (!decl_p && !def_p && TREE_CODE (decl) == TEMPLATE_DECL)

>>>>>      {

>>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>>> previously

>>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>>> template

>>>>> +     for the specialization and look it up in CLASS2LOC below.  

>>>>> For uses

>>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>>> points to

>>>>> +     the declaration of the specialization.  */

>>>>> +      type_decl = specialization_of (type_decl);

>>>>

>>>> Here shouldn't is_use be true?

>>>

>>> If it were set to true here we would find the partial specialization

>>> corresponding to its specialization in the use when what we want is

>>> the latter.  As a result, for the following:

>>>

>>>    template <class>   struct S;

>>>    template <class T> struct S<T*>;

>>>

>>>    extern class  S<int*> s1;   // expect -Wmismatched-tags

>>>    extern struct S<int*> s2;

>>>

>>> we'd end up with a warning for s2 pointing to the instantiation of

>>> s1 as the "guiding declaration:"

>>>

>>> z.C:5:15: warning: ‘template<class T> struct S<T*>’ declared with a 

>>> mismatched class-key ‘struct’ [-Wmismatched-tags]

>>>      5 | extern struct S<int*> s2;

>>>        |               ^~~~~~~

>>> z.C:5:15: note: remove the class-key or replace it with ‘class’

>>> z.C:4:15: note: ‘template<class T> struct S<T*>’ first declared as 

>>> ‘class’ here

>>>      4 | extern class  S<int*> s1;   // expect -Wmismatched-tags

>>>        |               ^~~~~~~

>>

>> I found this puzzling and wanted to see why that would be, but I can't 

>> reproduce it; compiling with -Wmismatched-tags produces only

> 

> I'm not sure what you did differently.  With the patch (the last one

> or the one in the attachment) we get the expected warning below.

> 

>>

>> wa2.C:4:17: warning: ‘S<T*>’ declared with a mismatched class-key 

>> ‘class’ [-Wmismatched-tags]

>>      4 |   extern class  S<int*> s1;   // expect -Wmismatched-tags

>>        |                 ^~~~~~~

>> wa2.C:4:17: note: remove the class-key or replace it with ‘struct’

>> wa2.C:2:29: note: ‘S<T*>’ first declared as ‘struct’ here

>>      2 |   template <class T> struct S<T*>;

>>

>> So the only difference is whether we talk about S<T*> or S<int*>.  I 

>> agree that the latter is probably better, which is what you get 

>> without my suggested change.  But since specialization_of does nothing 

>> if is_use is false, how about removing the call here and removing the 

>> is_use parameter?

> 

> Sure.

> 

>>

>>> +  if (tree spec = most_specialized_partial_spec (ret, tf_none))

>>> +    if (spec != error_mark_node)

>>> +      ret = TREE_VALUE (spec);

>>

>> I think you want to take the TREE_TYPE of the template here, so you 

>> don't need to do it here:

>>

>>> +      tree pt = specialization_of (TYPE_MAIN_DECL (type), true);

>>> +      if (TREE_CODE (pt) == TEMPLATE_DECL)

>>> +       pt = TREE_TYPE (pt);

>>> +      pt = TYPE_MAIN_DECL (pt);

>>

>> And also, since it takes a TYPE_DECL, it would be better to return 

>> another TYPE_DECL rather than a _TYPE, especially since the only 

>> caller immediately extracts a TYPE_DECL.

> 

> Okay.  Attached is an revised patch with these changes.

> 

> Martin
Andi Kleen via Gcc-patches March 26, 2020, 5:36 a.m. | #21
On 3/23/20 12:50 PM, Martin Sebor wrote:
> On 3/23/20 8:49 AM, Jason Merrill wrote:

>> On 3/21/20 5:59 PM, Martin Sebor wrote:

>>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is 

>>> false

>>> +     for alias definition.  */

>>> +      bool decl_class = (is_declaration

>>> +             && cp_parser_declares_only_class_p (parser));

>>>         cp_parser_check_class_key (parser, key_loc, tag_type, type, 

>>> false,

>>>                    cp_parser_declares_only_class_p (parser));

>>

>> Don't you need to use the new variable?

>>

>> Don't your testcases exercise this?

> 

> Of course they do.  This was a leftover from an experiment after I put

> the initial updated patch together.  On final review I decided to adjust

> some comments and forgot to restore the original use of the variable.

> 

>>> +      /* When TYPE is the use of an implicit specialization of a 

>>> previously

>>> +     declared template set TYPE_DECL to the type of the primary 

>>> template

>>> +     for the specialization and look it up in CLASS2LOC below.  For 

>>> uses

>>> +     of explicit or partial specializations TYPE_DECL already points to

>>> +     the declaration of the specialization.

>>> +     IS_USE is clear so that the type of an implicit instantiation 

>>> rather

>>> +     than that of a partial specialization is determined.  */

>>> +      type_decl = TREE_TYPE (type_decl);

>>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>>> +    type_decl = TYPE_MAIN_DECL (type_decl);

>>

>> The comment is no longer relevant to the code.  The remaining code 

>> also seems like it would have no effect; we already know type_decl is 

>> TYPE_MAIN_DECL (type).

> 

> I removed the block of code.

> 

> Martin

> 

> PS I would have preferred to resolve just the reported problem in this

> patch and deal with the template specializations more fully (and with

> aliases) in a followup.  As it is, it has grown bigger and more complex

> than I'm comfortable with, especially with the template specializations,

> harder for me to follow, and obviously a lot more time-consuming not

> just to put together but also to review.  Although this revision handles

> many more template specialization cases correctly, there still are other

> (arguably corner) cases that it doesn't.  I suspect getting those right

> might even require a design change, which I see as out of scope at this

> time (not to mention my ability).


Sure, at this point in the cycle there's always a tradeoff between 
better functionality and risk from ballooning changes.  It looks like 
the improved template handling could still be split out into a separate 
patch, if you'd prefer.

> +  /* Number of usesn of the class.  */

Typo.

> +     definintion if one exists or the first declaration otherwise.  */

typo.

> +  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))

...
> +	 the first reference to the instantiation.  The primary must

> +	 be (and inevitably is) at index zero.  */


I think CLASSTYPE_IMPLICIT_INSTANTIATION is more readable than testing 
the value 1.

What is the !is_decl (0) intended to do?  Changing it to an assert 
inside the 'if' doesn't seem to affect any of the testcases.

> +     For implicit instantiations of a primary template it's

> +     the class-key used to declare the primary with.  The primary

> +     must be at index zero.  */

> +  const tag_types xpect_key

> +    = cdlguide->class_key (cdlguide == this ? idxguide : 0);


A template can also be declared before it's defined; I think you want to 
move the def_p/idxdef/idxguide logic into another member function that 
you invoke on cdlguide to perhaps get the class_key_loc_t that you want 
to use as the pattern.

Jason
Andi Kleen via Gcc-patches March 26, 2020, 6:58 p.m. | #22
On 3/25/20 11:36 PM, Jason Merrill wrote:
> On 3/23/20 12:50 PM, Martin Sebor wrote:

>> On 3/23/20 8:49 AM, Jason Merrill wrote:

>>> On 3/21/20 5:59 PM, Martin Sebor wrote:

>>>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is 

>>>> false

>>>> +     for alias definition.  */

>>>> +      bool decl_class = (is_declaration

>>>> +             && cp_parser_declares_only_class_p (parser));

>>>>         cp_parser_check_class_key (parser, key_loc, tag_type, type, 

>>>> false,

>>>>                    cp_parser_declares_only_class_p (parser));

>>>

>>> Don't you need to use the new variable?

>>>

>>> Don't your testcases exercise this?

>>

>> Of course they do.  This was a leftover from an experiment after I put

>> the initial updated patch together.  On final review I decided to adjust

>> some comments and forgot to restore the original use of the variable.

>>

>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>> previously

>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>> template

>>>> +     for the specialization and look it up in CLASS2LOC below.  For 

>>>> uses

>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>> points to

>>>> +     the declaration of the specialization.

>>>> +     IS_USE is clear so that the type of an implicit instantiation 

>>>> rather

>>>> +     than that of a partial specialization is determined.  */

>>>> +      type_decl = TREE_TYPE (type_decl);

>>>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>>>> +    type_decl = TYPE_MAIN_DECL (type_decl);

>>>

>>> The comment is no longer relevant to the code.  The remaining code 

>>> also seems like it would have no effect; we already know type_decl is 

>>> TYPE_MAIN_DECL (type).

>>

>> I removed the block of code.

>>

>> Martin

>>

>> PS I would have preferred to resolve just the reported problem in this

>> patch and deal with the template specializations more fully (and with

>> aliases) in a followup.  As it is, it has grown bigger and more complex

>> than I'm comfortable with, especially with the template specializations,

>> harder for me to follow, and obviously a lot more time-consuming not

>> just to put together but also to review.  Although this revision handles

>> many more template specialization cases correctly, there still are other

>> (arguably corner) cases that it doesn't.  I suspect getting those right

>> might even require a design change, which I see as out of scope at this

>> time (not to mention my ability).

> 

> Sure, at this point in the cycle there's always a tradeoff between 

> better functionality and risk from ballooning changes.  It looks like 

> the improved template handling could still be split out into a separate 

> patch, if you'd prefer.


I would prefer to get this patch committed as is now.  I appreciate
there are improvements that can be made to the code (there always
are) but, unlike the bugs it fixes, they are invisible to users and
so don't seem essential at this point.

>> +  /* Number of usesn of the class.  */

> Typo.

> 

>> +     definintion if one exists or the first declaration otherwise.  */

> typo.

> 

>> +  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))

> ...

>> +     the first reference to the instantiation.  The primary must

>> +     be (and inevitably is) at index zero.  */

> 

> I think CLASSTYPE_IMPLICIT_INSTANTIATION is more readable than testing 

> the value 1.


Okay.

> 

> What is the !is_decl (0) intended to do?  Changing it to an assert 

> inside the 'if' doesn't seem to affect any of the testcases.


Looks like the test is an unnecessary remnant and can be removed.
In fact, both is_decl() and decl_p member don't appear necessary
anymore so I've removed them too.

>> +     For implicit instantiations of a primary template it's

>> +     the class-key used to declare the primary with.  The primary

>> +     must be at index zero.  */

>> +  const tag_types xpect_key

>> +    = cdlguide->class_key (cdlguide == this ? idxguide : 0);

> 

> A template can also be declared before it's defined;


Obviously, just like a class.  Is there something you expect me to
change in response to this point?

> I think you want to 

> move the def_p/idxdef/idxguide logic into another member function that 

> you invoke on cdlguide to perhaps get the class_key_loc_t that you want 

> to use as the pattern.


I'm not quite sure what you have in mind here.  I agree the cdlcode
code looks a little cumbersome and perhaps could be restructured but
it's not obvious to me how.  Nothing I tried looked like a clear win
so unless you consider changing how this is done a prerequisite for
accepting the whole patch I'd rather not spend any more time at this
stage iterating over refinements of it.  Please let me know soon.

Attached is a revised patch with the other changes above.

Martin
PR c++/94078 - bogus and missing -Wmismatched-tags on an instance of a template
PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* cp-tree.h (most_specialized_partial_spec): Declare.
	* parser.c (cp_parser_elaborated_type_specifier): Distinguish alias
	from declarations.
	(specialization_of): New function.
	(cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	Correct handling of template specializations.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.  Use primary template or partial
	specialization as the guide for uses of implicit instantiations.
	* pt.c (most_specialized_partial_spec): Declare extern.

gcc/testsuite/ChangeLog:

	PR c++/94078
	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wmismatched-tags-4.C: New test.
	* g++.dg/warn/Wmismatched-tags-5.C: New test.
	* g++.dg/warn/Wmismatched-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.
	* g++.dg/warn/Wredundant-tags-7.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0783b3114f2..9c4447202fc 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6944,6 +6944,7 @@ extern int comp_template_args			(tree, tree, tree * = NULL,
 extern int template_args_equal                  (tree, tree, bool = false);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
+extern tree most_specialized_partial_spec       (tree, tsubst_flags_t);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, int, tree, tree,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index cbd5510a8fb..94e45627cc7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -18937,9 +18937,12 @@ cp_parser_elaborated_type_specifier (cp_parser* parser,
     cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key);
   else
     {
-      /* Diagnose class/struct/union mismatches.  */
+      /* Diagnose class/struct/union mismatches.  IS_DECLARATION is false
+	 for alias definition.  */
+      bool decl_class = (is_declaration
+			 && cp_parser_declares_only_class_p (parser));
       cp_parser_check_class_key (parser, key_loc, tag_type, type, false,
-				 cp_parser_declares_only_class_p (parser));
+				 decl_class);
 
       /* Indicate whether this class was declared as a `class' or as a
 	 `struct'.  */
@@ -30922,8 +30925,9 @@ class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
@@ -30956,7 +30960,8 @@ private:
   struct class_key_loc_t
   {
     class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant)
-      : func (func), loc (loc), class_key (key), key_redundant (redundant) { }
+      : func (func), loc (loc), class_key (key), key_redundant (redundant)
+    { }
 
     /* The function the type is mentioned in.  */
     tree func;
@@ -31019,6 +31024,40 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Returns the template or specialization of one to which the RECORD_TYPE
+   TYPE corresponds.  */
+
+static tree
+specialization_of (tree type)
+{
+  tree ret = type;
+
+  /* Determine the template or its partial specialization to which TYPE
+     corresponds.  */
+  if (tree spec = most_specialized_partial_spec (type, tf_none))
+    if (spec != error_mark_node)
+      ret = TREE_TYPE (TREE_VALUE (spec));
+
+  if (ret == type)
+    ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+
+  return TYPE_MAIN_DECL (ret);
+}
+
+
+/* Adds the class TYPE to the collection of class decls and diagnoses
+   redundant tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a (likely, based on syntactic context) declaration of class
+   TYPE and clear for a reference to it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31030,7 +31069,10 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31048,29 +31090,8 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
-    {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
-    }
-
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it must
+     exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31080,30 +31101,52 @@ class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
 	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled hdeader.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant,
+				   def_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31156,35 +31199,74 @@ class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
-  unsigned ndecls = locvec.length ();
+  if (!warn_mismatched_tags)
+    return;
 
-  /* Skip a declaration that consistently uses the same class-key
-     or one with just a solitary declaration (i.e., TYPE_DECL).  */
-  if (def_class_key != none_type || ndecls < 2)
+  /* Number of uses of the class.  */
+  const unsigned ndecls = locvec.length ();
+  if (ndecls < 2)
     return;
 
-  /* Save the current function before changing it below.  */
-  tree save_func = current_function_decl;
-  /* Set if a class definition for RECLOC has been seen.  */
-  bool def_p = idxdef < ndecls;
-  unsigned idxguide = def_p ? idxdef : 0;
+  /* Set if a definition for the class has been seen.  */
+  const bool def_p = idxdef < ndecls;
+
+  /* The class (or template) declaration guiding the decisions about
+     the diagnostic.  For ordinary classes it's the same as THIS.  For
+     uses of instantiations of templates other than their declarations
+     it points to the record for the declaration of the corresponding
+     primary template or partial specialization.  */
+  class_decl_loc_t *cdlguide = this;
+
+  tree type = TREE_TYPE (type_decl);
+  if (CLASSTYPE_IMPLICIT_INSTANTIATION (type))
+    {
+      /* For implicit instantiations of a primary template look up
+	 the primary or partial specialization and use it as
+	 the expected class-key rather than using the class-key of
+	 the first reference to the instantiation.  The primary must
+	 be (and inevitably is) at index zero.  */
+      tree spec = specialization_of (type);
+      cdlguide = class2loc.get (spec);
+      gcc_assert (cdlguide != NULL);
+    }
+  else
+    {
+      /* Skip declarations that consistently use the same class-key.  */
+      if (def_class_key != none_type)
+	return;
+    }
+
+  /* The index of the declaration whose class-key this declaration
+     is expected to match.  It's either the class-key of the class
+     definition if one exists or the first declaration otherwise.  */
+  const unsigned idxguide = def_p ? idxdef : 0;
   unsigned idx = 0;
   /* Advance IDX to the first declaration that either is not
      a definition or that doesn't match the first declaration
      if no definition is provided.  */
-  while (class_key (idx) == class_key (idxguide))
+  while (class_key (idx) == cdlguide->class_key (idxguide))
     if (++idx == ndecls)
       return;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
-     if no definition has been provided.  */
-  tag_types xpect_key = class_key (def_p ? idxguide : 0);
-  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
-  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+     if no definition has been provided.
+     For implicit instantiations of a primary template it's
+     the class-key used to declare the primary with.  The primary
+     must be at index zero.  */
+  const tag_types xpect_key
+    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
+  if (cdlguide != this && xpect_key == class_key (idx))
+    return;
+
+  /* Save the current function before changing it below.  */
+  tree save_func = current_function_decl;
   /* Set the function declaration to print in diagnostic context.  */
   current_function_decl = function (idx);
 
+  const char *xmatchkstr = xpect_key == record_type ? "class" : "struct";
+  const char *xpectkstr = xpect_key == record_type ? "struct" : "class";
+
   location_t loc = location (idx);
   bool key_redundant_p = key_redundant (idx);
   auto_diagnostic_group d;
@@ -31205,7 +31287,7 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Also point to the first declaration or definition that guided
      the decision to issue the warning above.  */
-  inform (location (idxguide),
+  inform (cdlguide->location (idxguide),
 	  (def_p
 	   ? G_("%qT defined as %qs here")
 	   : G_("%qT first declared as %qs here")),
@@ -31247,25 +31329,29 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
-  /* Save the current function before changing it below.  It should
+  /* Save the current function before changing on return.  It should
      be null at this point.  */
-  tree save_func = current_function_decl;
+  temp_override<tree> cleanup (current_function_decl);
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct/template declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
-  /* Restore the current function.  */
-  current_function_decl = save_func;
 }
 
 /* Issue an error message if DECL is redeclared with different
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 03a8dfbd37c..0a2c4c86088 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -185,7 +185,7 @@ static int unify_pack_expansion (tree, tree, tree,
 				 tree, unification_kind_t, bool, bool);
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
-static tree most_specialized_partial_spec (tree, tsubst_flags_t);
+tree most_specialized_partial_spec (tree, tsubst_flags_t);
 static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@@ -24332,7 +24332,7 @@ most_general_template (tree decl)
    partial specializations matching TARGET, then NULL_TREE is
    returned, indicating that the primary template should be used.  */
 
-static tree
+tree
 most_specialized_partial_spec (tree target, tsubst_flags_t complain)
 {
   tree list = NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
new file mode 100644
index 00000000000..7570264b1d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-4.C
@@ -0,0 +1,141 @@
+/* PR c++/94078 - bogus and missing -Wmismatched-tags on an instance
+   of a template
+   Verify that -Wmismatched-tags is issued for redeclarations and
+   instances of the appropriate primary template or specialization.
+  { dg-do compile }
+  { dg-options "-Wmismatched-tags" } */
+
+// Exercise explicit specialization.
+template <class> class S1;
+template <>      struct S1<int>;
+
+template <class> class S1;
+template <class> struct S1;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<char>;
+template <>      struct S1<char>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <>      class S1<int>;       // { dg-warning "\\\[-Wmismatched-tags" }
+template <>      struct S1<int>;
+
+extern        S1<void> s1v;
+extern class  S1<void> s1v;
+extern struct S1<void> s1v;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S1<int> s1i;
+extern class  S1<int> s1i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S1<int> s1i;
+
+extern        S1<char> s1c;
+extern class  S1<char> s1c;
+extern struct S1<char> s1c;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+// Exercise partial specialization.
+template <class>   struct S2;
+template <class T> class S2<const T>;
+
+template <class>   class S2;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S2;
+
+template <class T> class S2<const T>;
+template <class T> struct S2<const T>;// { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S2<int> s2i;
+extern class  S2<int> s2i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S2<int> s2i;
+
+extern        S2<const int> s2ci;
+extern class  S2<const int> s2ci;
+extern struct S2<const int> s2ci;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+template <class>   struct S3;
+template <class T> class S3<T*>;
+template <class T> struct S3<T&>;
+
+template <class>   class S3;          // { dg-warning "\\\[-Wmismatched-tags" }
+template <class>   struct S3;
+
+template <class T> class S3<T*>;
+template <class T> struct S3<T*>;     // { dg-warning "\\\[-Wmismatched-tags" }
+
+template <class T> class S3<T&>;      // { dg-warning "\\\[-Wmismatched-tags" }
+template <class T> struct S3<T&>;
+
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat exactly the same as above.
+extern        S3<int> s3i;
+extern class  S3<int> s3i;            // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int> s3i;
+
+extern        S3<int*> s3p;
+extern class  S3<int*> s3p;
+extern struct S3<int*> s3p;           // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<int&> s3r;
+extern class  S3<int&> s3r;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<int&> s3r;
+
+// Repeat the same as above just with different type.
+extern        S3<long> s3l;
+extern class  S3<long> s3l;           // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long> s3l;
+
+extern        S3<long*> s3lp;
+extern class  S3<long*> s3lp;
+extern struct S3<long*> s3lp;         // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long&> s3lr;
+extern class  S3<long&> s3lr;         // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct S3<long&> s3lr;
+
+// Repeat with the class-keys swapped.
+extern        S3<long> s3l;
+extern struct S3<long> s3l;
+extern class  S3<long> s3l;          // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern        S3<long*> s3lp;
+extern struct S3<long*> s3lp;        // { dg-warning "\\\[-Wmismatched-tags" }
+extern class  S3<long*> s3lp;
+
+extern        S3<long&> s3lr;
+extern struct S3<long&> s3lr;
+extern class  S3<long&> s3lr;        // { dg-warning "\\\[-Wmismatched-tags" }
+
+
+namespace N
+{
+template <class> struct A;
+
+extern class A<int> ai;               // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct A<int> ai;
+
+typedef class A<int> AI;              // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct A<int> AI;
+
+template <class> struct B;
+template <> class B<int>;
+template <> struct B<char>;
+
+extern class B<int> bi;
+extern struct B<int> bi;              // { dg-warning "\\\[-Wmismatched-tags" }
+
+extern class B<char> bc;              // { dg-warning "\\\[-Wmismatched-tags" }
+extern struct B<char> bc;
+
+typedef class B<char> BC;             // { dg-warning "\\\[-Wmismatched-tags" }
+typedef struct B<char> BC;
+
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
new file mode 100644
index 00000000000..622b9e386ab
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-5.C
@@ -0,0 +1,117 @@
+/* PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef
+   of an implicit class template specialization
+  { dg-do compile }
+  { dg-options "-Wall -Wmismatched-tags" }
+  { dg-require-effective-target c++11 } */
+
+class A;                                // { dg-message "declared as 'class'" }
+typedef        A A0;
+typedef class  A A0;
+typedef struct A A0;                    // { dg-warning "-Wmismatched-tags" }
+
+template <int> struct B;                // { dg-message "declared as 'struct'" }
+typedef        B<0> B0;
+typedef class  B<0> B0;                 // { dg-warning "-Wmismatched-tags" }
+typedef struct B<0> B0;
+
+
+// Exercise member types of templates with non-type arguments.
+template <int> struct CN;               // { dg-message "declared as 'struct'" }
+
+template <int N>
+struct X_CNp1 {
+  typedef CN<N + 1> CNp1;
+};
+
+template <int N>
+struct X_class_CNp1 {
+  typedef class CN<N + 1> CNp1;         // { dg-warning "-Wmismatched-tags" }
+};
+
+template <int N>
+struct X_struct_CNp1 {
+  typedef struct CN<N + 1> CNp1;
+};
+
+
+// Exercise partial specialization of templates with member types.
+template <class> class CT1;
+template <class T> struct CT1<T*> { };
+template <class T> struct CT1<T**> { };
+template <class T> class  CT1<T***> { };
+
+template <class> struct CT2;
+template <class T> struct CT2<T*> {
+  // Expect class-key to match the primary.
+         CT1<T> ct1_0;
+  class  CT1<T> ct1_1;
+  struct CT1<T> ct1_2;                  // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to match the CT1<T*> partial specialization.
+         CT1<T*> ct1p1_0;
+  class  CT1<T*> ct1p1_1;               // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T*> ct1p1_2;
+
+  // Expect class-key to match the CT1<T**> partial specialization.
+         CT1<T**> ct1p2_0;
+  class  CT1<T**> ct1p2_1;              // { dg-warning "-Wmismatched-tags" }
+  struct CT1<T**> ct1p2_2;
+
+  // Expect class-key to match the CT1<T***> partial specialization.
+         CT1<T***> ct1p3_0;
+  class  CT1<T***> ct1p3_1;
+  struct CT1<T***> ct1p3_2;             // { dg-warning "-Wmismatched-tags" }
+
+  // Expect class-key to still match the CT1<T***> partial specialization.
+         CT1<T****> ct1p4_0;
+  class  CT1<T****> ct1p4_1;
+  struct CT1<T****> ct1p4_2;            // { dg-warning "-Wmismatched-tags" }
+};
+
+// Exercise many partial specializations (since the class-key for each
+// must be tracked separately from the others).
+template <class>   class  D;
+template <class T> struct D<T*>;
+template <class T> class  D<T&>;
+template <class T> struct D<const T*>;
+template <class T> class  D<const T&>;
+template <class T> struct D<volatile T*>;
+template <class T> class  D<volatile T&>;
+template <class T> struct D<const volatile T*>;
+template <class T> class  D<const volatile T&>;
+
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+typedef class  D<int*> DIP;             // { dg-warning "-Wmismatched-tags" }
+typedef struct D<int*> DIP;
+
+typedef class  D<int&>  DIR;
+typedef struct D<int&> DIR;             // { dg-warning "-Wmismatched-tags" }
+typedef class  D<int&>  DIR;
+
+
+typedef struct D<const int*> DCIP;
+typedef class  D<const int*> DCIP;      // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const int*> DCIP;
+
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const int&>  DCIR;
+typedef struct D<const int&> DCIR;      // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<volatile int*> DVIP;
+typedef class  D<volatile int*> DVIP;   // { dg-warning "-Wmismatched-tags" }
+typedef struct D<volatile int*> DVIP;
+
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+typedef class  D<volatile int&> DVIR;
+typedef struct D<volatile int&> DVIR;   // { dg-warning "-Wmismatched-tags" }
+
+
+typedef struct D<const volatile int*> DCVIP;
+typedef class  D<const volatile int*> DCVIP;    // { dg-warning "-Wmismatched-tags" }
+typedef struct D<const volatile int*> DCVIP;
+
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
+typedef class  D<const volatile int&> DCVIR;
+typedef struct D<const volatile int&> DCVIR;    // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
new file mode 100644
index 00000000000..d9d644c4226
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-6.C
@@ -0,0 +1,29 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+class A;                      // { dg-message "declared as 'class'" }
+using AA =        A;
+using AA = class  A;
+using AA = struct A;          // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class B;     // { dg-message "declared as 'class'" }
+
+using Bi =        B<int>;
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+using Bi = class  B<int>;
+using Bi = struct B<int>;     // { dg-warning "-Wmismatched-tags" }
+
+
+template <class> class C;     // { dg-message "declared as 'class'" }
+
+template <class T> using Cp = C<T*>;
+template <class T> using Cp = class C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
+
+template <class T> using Cp = class  C<T*>;
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wmismatched-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@ union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..ce5cb967b79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,91 @@
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 T_S3;           // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 T_S4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <int> struct TS1;
+
+// Verify redeclaration with no definition.
+template <> struct TS1<0>;
+template <> struct TS1<0>;
+
+// Verify definition after a declaration and vice versa.
+template <> struct TS1<1>;
+template <> struct TS1<1> { };
+template <> struct TS1<1>;
+
+// Verify typedefs of an explicit specialization with a definition.
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<1> TS1_1;
+typedef struct TS1<1> TS1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an expplicit specialization.
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<1> ts1_1;
+extern struct TS1<1> ts1_1;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify typedefs of an implicit specialization without a definition.
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+typedef        TS1<2> TS1_2;
+typedef struct TS1<2> TS1_2;      // { dg-warning "\\\[-Wredundant-tags" }
+
+// Verify object declarations of an implicit specialization.
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+extern        TS1<2> ts1_2;
+extern struct TS1<2> ts1_2;       // { dg-warning "\\\[-Wredundant-tags" }
+
+
+// Verify partial template specialization.
+template <class> struct TS2;
+template <class T> struct TS2<const T>;
+template <class T> struct TS2<volatile T>;
+
+template <class T>
+struct TS4
+{
+  typedef struct TS2<const T> TS2_CT1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<const T> TS2_CT2;
+
+  typedef struct TS2<T> TS2_T1;   // { dg-warning "\\\[-Wredundant-tags" }
+  typedef        TS2<T> TS2_T2;
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
new file mode 100644
index 00000000000..872535b9221
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-7.C
@@ -0,0 +1,25 @@
+/* Verify -Wmismatched-tags on alias definitions.
+   { dg-do compile { target c++11 } }
+   { dg-options "-Wall -Wredundant-tags" } */
+
+class A;
+using AA =        A;
+using AA = class  A;          // { dg-warning "-Wredundant-tags" }
+using AA = struct A;          // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class B;
+
+using Bi =        B<int>;
+using Bi = class  B<int>;     // { dg-warning "-Wredundant-tags" }
+using Bi = struct B<int>;     // { dg-warning "-Wredundant-tags" }
+
+
+template <class> class C;
+
+template <class T>
+using Cp = C<T*>;
+template <class T>
+using Cp = class C<T*>;       // { dg-warning "-Wredundant-tags" }
+template <class T>
+using Cp = struct C<T*>;      // { dg-warning "-Wredundant-tags" }
Andi Kleen via Gcc-patches March 26, 2020, 10:16 p.m. | #23
On 3/26/20 2:58 PM, Martin Sebor wrote:
> On 3/25/20 11:36 PM, Jason Merrill wrote:

>> On 3/23/20 12:50 PM, Martin Sebor wrote:

>>> On 3/23/20 8:49 AM, Jason Merrill wrote:

>>>> On 3/21/20 5:59 PM, Martin Sebor wrote:

>>>>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION 

>>>>> is false

>>>>> +     for alias definition.  */

>>>>> +      bool decl_class = (is_declaration

>>>>> +             && cp_parser_declares_only_class_p (parser));

>>>>>         cp_parser_check_class_key (parser, key_loc, tag_type, type, 

>>>>> false,

>>>>>                    cp_parser_declares_only_class_p (parser));

>>>>

>>>> Don't you need to use the new variable?

>>>>

>>>> Don't your testcases exercise this?

>>>

>>> Of course they do.  This was a leftover from an experiment after I put

>>> the initial updated patch together.  On final review I decided to adjust

>>> some comments and forgot to restore the original use of the variable.

>>>

>>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>>> previously

>>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>>> template

>>>>> +     for the specialization and look it up in CLASS2LOC below.  

>>>>> For uses

>>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>>> points to

>>>>> +     the declaration of the specialization.

>>>>> +     IS_USE is clear so that the type of an implicit instantiation 

>>>>> rather

>>>>> +     than that of a partial specialization is determined.  */

>>>>> +      type_decl = TREE_TYPE (type_decl);

>>>>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>>>>> +    type_decl = TYPE_MAIN_DECL (type_decl);

>>>>

>>>> The comment is no longer relevant to the code.  The remaining code 

>>>> also seems like it would have no effect; we already know type_decl 

>>>> is TYPE_MAIN_DECL (type).

>>>

>>> I removed the block of code.

>>>

>>> Martin

>>>

>>> PS I would have preferred to resolve just the reported problem in this

>>> patch and deal with the template specializations more fully (and with

>>> aliases) in a followup.  As it is, it has grown bigger and more complex

>>> than I'm comfortable with, especially with the template specializations,

>>> harder for me to follow, and obviously a lot more time-consuming not

>>> just to put together but also to review.  Although this revision handles

>>> many more template specialization cases correctly, there still are other

>>> (arguably corner) cases that it doesn't.  I suspect getting those right

>>> might even require a design change, which I see as out of scope at this

>>> time (not to mention my ability).

>>

>> Sure, at this point in the cycle there's always a tradeoff between 

>> better functionality and risk from ballooning changes.  It looks like 

>> the improved template handling could still be split out into a 

>> separate patch, if you'd prefer.

> 

> I would prefer to get this patch committed as is now.  I appreciate

> there are improvements that can be made to the code (there always

> are) but, unlike the bugs it fixes, they are invisible to users and

> so don't seem essential at this point.

> 

>>> +  /* Number of usesn of the class.  */

>> Typo.

>>

>>> +     definintion if one exists or the first declaration otherwise.  */

>> typo.

>>

>>> +  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))

>> ...

>>> +     the first reference to the instantiation.  The primary must

>>> +     be (and inevitably is) at index zero.  */

>>

>> I think CLASSTYPE_IMPLICIT_INSTANTIATION is more readable than testing 

>> the value 1.

> 

> Okay.

> 

>>

>> What is the !is_decl (0) intended to do?  Changing it to an assert 

>> inside the 'if' doesn't seem to affect any of the testcases.

> 

> Looks like the test is an unnecessary remnant and can be removed.

> In fact, both is_decl() and decl_p member don't appear necessary

> anymore so I've removed them too.

> 

>>> +     For implicit instantiations of a primary template it's

>>> +     the class-key used to declare the primary with.  The primary

>>> +     must be at index zero.  */

>>> +  const tag_types xpect_key

>>> +    = cdlguide->class_key (cdlguide == this ? idxguide : 0);

>>

>> A template can also be declared before it's defined;

> 

> Obviously, just like a class.  Is there something you expect me to

> change in response to this point?


You're hardcoding index zero for the template case, which seems to 
assume that the definition of a template is always at index zero.

>> I think you want to move the def_p/idxdef/idxguide logic into another 

>> member function that you invoke on cdlguide to perhaps get the 

>> class_key_loc_t that you want to use as the pattern.

> 

> I'm not quite sure what you have in mind here.  I agree the cdlcode

> code looks a little cumbersome and perhaps could be restructured but

> it's not obvious to me how.  Nothing I tried looked like a clear win

> so unless you consider changing how this is done a prerequisite for

> accepting the whole patch I'd rather not spend any more time at this

> stage iterating over refinements of it.  Please let me know soon.


I mean that

> +  const unsigned ndecls = locvec.length (); > +  const bool def_p = idxdef < ndecls;

> +  const unsigned idxguide = def_p ? idxdef : 0;


are based on whether the instantiation, rather than the template, is 
defined.

I's probably enough to update ndecls to cdlguide->locvec.length() and 
change the uses of idxdef to cdlguide->idxdef.  And then idxguide will 
be set properly for cdlguide, so the code farther above can become

> +  const tag_types xpect_key

> +    = cdlguide->class_key (idxguide);


If you'd prefer, I can make these changes and commit the patch myself.

Jason
Andi Kleen via Gcc-patches March 26, 2020, 10:51 p.m. | #24
On 3/26/20 4:16 PM, Jason Merrill wrote:
> On 3/26/20 2:58 PM, Martin Sebor wrote:

>> On 3/25/20 11:36 PM, Jason Merrill wrote:

>>> On 3/23/20 12:50 PM, Martin Sebor wrote:

>>>> On 3/23/20 8:49 AM, Jason Merrill wrote:

>>>>> On 3/21/20 5:59 PM, Martin Sebor wrote:

>>>>>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION 

>>>>>> is false

>>>>>> +     for alias definition.  */

>>>>>> +      bool decl_class = (is_declaration

>>>>>> +             && cp_parser_declares_only_class_p (parser));

>>>>>>         cp_parser_check_class_key (parser, key_loc, tag_type, 

>>>>>> type, false,

>>>>>>                    cp_parser_declares_only_class_p (parser));

>>>>>

>>>>> Don't you need to use the new variable?

>>>>>

>>>>> Don't your testcases exercise this?

>>>>

>>>> Of course they do.  This was a leftover from an experiment after I put

>>>> the initial updated patch together.  On final review I decided to 

>>>> adjust

>>>> some comments and forgot to restore the original use of the variable.

>>>>

>>>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>>>> previously

>>>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>>>> template

>>>>>> +     for the specialization and look it up in CLASS2LOC below. 

>>>>>> For uses

>>>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>>>> points to

>>>>>> +     the declaration of the specialization.

>>>>>> +     IS_USE is clear so that the type of an implicit 

>>>>>> instantiation rather

>>>>>> +     than that of a partial specialization is determined.  */

>>>>>> +      type_decl = TREE_TYPE (type_decl);

>>>>>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>>>>>> +    type_decl = TYPE_MAIN_DECL (type_decl);

>>>>>

>>>>> The comment is no longer relevant to the code.  The remaining code 

>>>>> also seems like it would have no effect; we already know type_decl 

>>>>> is TYPE_MAIN_DECL (type).

>>>>

>>>> I removed the block of code.

>>>>

>>>> Martin

>>>>

>>>> PS I would have preferred to resolve just the reported problem in this

>>>> patch and deal with the template specializations more fully (and with

>>>> aliases) in a followup.  As it is, it has grown bigger and more complex

>>>> than I'm comfortable with, especially with the template 

>>>> specializations,

>>>> harder for me to follow, and obviously a lot more time-consuming not

>>>> just to put together but also to review.  Although this revision 

>>>> handles

>>>> many more template specialization cases correctly, there still are 

>>>> other

>>>> (arguably corner) cases that it doesn't.  I suspect getting those right

>>>> might even require a design change, which I see as out of scope at this

>>>> time (not to mention my ability).

>>>

>>> Sure, at this point in the cycle there's always a tradeoff between 

>>> better functionality and risk from ballooning changes.  It looks like 

>>> the improved template handling could still be split out into a 

>>> separate patch, if you'd prefer.

>>

>> I would prefer to get this patch committed as is now.  I appreciate

>> there are improvements that can be made to the code (there always

>> are) but, unlike the bugs it fixes, they are invisible to users and

>> so don't seem essential at this point.

>>

>>>> +  /* Number of usesn of the class.  */

>>> Typo.

>>>

>>>> +     definintion if one exists or the first declaration otherwise.  */

>>> typo.

>>>

>>>> +  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))

>>> ...

>>>> +     the first reference to the instantiation.  The primary must

>>>> +     be (and inevitably is) at index zero.  */

>>>

>>> I think CLASSTYPE_IMPLICIT_INSTANTIATION is more readable than 

>>> testing the value 1.

>>

>> Okay.

>>

>>>

>>> What is the !is_decl (0) intended to do?  Changing it to an assert 

>>> inside the 'if' doesn't seem to affect any of the testcases.

>>

>> Looks like the test is an unnecessary remnant and can be removed.

>> In fact, both is_decl() and decl_p member don't appear necessary

>> anymore so I've removed them too.

>>

>>>> +     For implicit instantiations of a primary template it's

>>>> +     the class-key used to declare the primary with.  The primary

>>>> +     must be at index zero.  */

>>>> +  const tag_types xpect_key

>>>> +    = cdlguide->class_key (cdlguide == this ? idxguide : 0);

>>>

>>> A template can also be declared before it's defined;

>>

>> Obviously, just like a class.  Is there something you expect me to

>> change in response to this point?

> 

> You're hardcoding index zero for the template case, which seems to 

> assume that the definition of a template is always at index zero.


Sounds like you're concerned about something like:

   template <class> class A;
   template <class T> class A<T*>;        // expect -Wmismatched-tags
   template <class T> struct A<T*> { };   // ...because of this
   class A<int*> a;                       // expect -Wmismatched-tags

The definition should be at index zero because once it's seen, entries
for prior declarations are purged (after diagnosing mismatches).  But
I'm sure the tests for these things could stand to beefed up so there
could easily be cases that aren't handled correctly.

> 

>>> I think you want to move the def_p/idxdef/idxguide logic into another 

>>> member function that you invoke on cdlguide to perhaps get the 

>>> class_key_loc_t that you want to use as the pattern.

>>

>> I'm not quite sure what you have in mind here.  I agree the cdlcode

>> code looks a little cumbersome and perhaps could be restructured but

>> it's not obvious to me how.  Nothing I tried looked like a clear win

>> so unless you consider changing how this is done a prerequisite for

>> accepting the whole patch I'd rather not spend any more time at this

>> stage iterating over refinements of it.  Please let me know soon.

> 

> I mean that

> 

>> +  const unsigned ndecls = locvec.length (); > +  const bool def_p = 

>> idxdef < ndecls;

>> +  const unsigned idxguide = def_p ? idxdef : 0;

> 

> are based on whether the instantiation, rather than the template, is 

> defined.

> 

> I's probably enough to update ndecls to cdlguide->locvec.length() and 

> change the uses of idxdef to cdlguide->idxdef.  And then idxguide will 

> be set properly for cdlguide, so the code farther above can become

> 

>> +  const tag_types xpect_key

>> +    = cdlguide->class_key (idxguide);

> 

> If you'd prefer, I can make these changes and commit the patch myself.


Please go ahead.  That will make it a lot quicker.

Martin
Andi Kleen via Gcc-patches March 27, 2020, 4:33 p.m. | #25
On 3/26/20 6:51 PM, Martin Sebor wrote:
> On 3/26/20 4:16 PM, Jason Merrill wrote:

>> On 3/26/20 2:58 PM, Martin Sebor wrote:

>>> On 3/25/20 11:36 PM, Jason Merrill wrote:

>>>> On 3/23/20 12:50 PM, Martin Sebor wrote:

>>>>> On 3/23/20 8:49 AM, Jason Merrill wrote:

>>>>>> On 3/21/20 5:59 PM, Martin Sebor wrote:

>>>>>>> +      /* Diagnose class/struct/union mismatches.  IS_DECLARATION 

>>>>>>> is false

>>>>>>> +     for alias definition.  */

>>>>>>> +      bool decl_class = (is_declaration

>>>>>>> +             && cp_parser_declares_only_class_p (parser));

>>>>>>>         cp_parser_check_class_key (parser, key_loc, tag_type, 

>>>>>>> type, false,

>>>>>>>                    cp_parser_declares_only_class_p (parser));

>>>>>>

>>>>>> Don't you need to use the new variable?

>>>>>>

>>>>>> Don't your testcases exercise this?

>>>>>

>>>>> Of course they do.  This was a leftover from an experiment after I put

>>>>> the initial updated patch together.  On final review I decided to 

>>>>> adjust

>>>>> some comments and forgot to restore the original use of the variable.

>>>>>

>>>>>>> +      /* When TYPE is the use of an implicit specialization of a 

>>>>>>> previously

>>>>>>> +     declared template set TYPE_DECL to the type of the primary 

>>>>>>> template

>>>>>>> +     for the specialization and look it up in CLASS2LOC below. 

>>>>>>> For uses

>>>>>>> +     of explicit or partial specializations TYPE_DECL already 

>>>>>>> points to

>>>>>>> +     the declaration of the specialization.

>>>>>>> +     IS_USE is clear so that the type of an implicit 

>>>>>>> instantiation rather

>>>>>>> +     than that of a partial specialization is determined.  */

>>>>>>> +      type_decl = TREE_TYPE (type_decl);

>>>>>>> +      if (TREE_CODE (type_decl) != TEMPLATE_DECL)

>>>>>>> +    type_decl = TYPE_MAIN_DECL (type_decl);

>>>>>>

>>>>>> The comment is no longer relevant to the code.  The remaining code 

>>>>>> also seems like it would have no effect; we already know type_decl 

>>>>>> is TYPE_MAIN_DECL (type).

>>>>>

>>>>> I removed the block of code.

>>>>>

>>>>> Martin

>>>>>

>>>>> PS I would have preferred to resolve just the reported problem in this

>>>>> patch and deal with the template specializations more fully (and with

>>>>> aliases) in a followup.  As it is, it has grown bigger and more 

>>>>> complex

>>>>> than I'm comfortable with, especially with the template 

>>>>> specializations,

>>>>> harder for me to follow, and obviously a lot more time-consuming not

>>>>> just to put together but also to review.  Although this revision 

>>>>> handles

>>>>> many more template specialization cases correctly, there still are 

>>>>> other

>>>>> (arguably corner) cases that it doesn't.  I suspect getting those 

>>>>> right

>>>>> might even require a design change, which I see as out of scope at 

>>>>> this

>>>>> time (not to mention my ability).

>>>>

>>>> Sure, at this point in the cycle there's always a tradeoff between 

>>>> better functionality and risk from ballooning changes.  It looks 

>>>> like the improved template handling could still be split out into a 

>>>> separate patch, if you'd prefer.

>>>

>>> I would prefer to get this patch committed as is now.  I appreciate

>>> there are improvements that can be made to the code (there always

>>> are) but, unlike the bugs it fixes, they are invisible to users and

>>> so don't seem essential at this point.

>>>

>>>>> +  /* Number of usesn of the class.  */

>>>> Typo.

>>>>

>>>>> +     definintion if one exists or the first declaration 

>>>>> otherwise.  */

>>>> typo.

>>>>

>>>>> +  if (CLASSTYPE_USE_TEMPLATE (type) == 1 && !is_decl (0))

>>>> ...

>>>>> +     the first reference to the instantiation.  The primary must

>>>>> +     be (and inevitably is) at index zero.  */

>>>>

>>>> I think CLASSTYPE_IMPLICIT_INSTANTIATION is more readable than 

>>>> testing the value 1.

>>>

>>> Okay.

>>>

>>>>

>>>> What is the !is_decl (0) intended to do?  Changing it to an assert 

>>>> inside the 'if' doesn't seem to affect any of the testcases.

>>>

>>> Looks like the test is an unnecessary remnant and can be removed.

>>> In fact, both is_decl() and decl_p member don't appear necessary

>>> anymore so I've removed them too.

>>>

>>>>> +     For implicit instantiations of a primary template it's

>>>>> +     the class-key used to declare the primary with.  The primary

>>>>> +     must be at index zero.  */

>>>>> +  const tag_types xpect_key

>>>>> +    = cdlguide->class_key (cdlguide == this ? idxguide : 0);

>>>>

>>>> A template can also be declared before it's defined;

>>>

>>> Obviously, just like a class.  Is there something you expect me to

>>> change in response to this point?

>>

>> You're hardcoding index zero for the template case, which seems to 

>> assume that the definition of a template is always at index zero.

> 

> Sounds like you're concerned about something like:

> 

>    template <class> class A;

>    template <class T> class A<T*>;        // expect -Wmismatched-tags

>    template <class T> struct A<T*> { };   // ...because of this

>    class A<int*> a;                       // expect -Wmismatched-tags

> 

> The definition should be at index zero because once it's seen, entries

> for prior declarations are purged (after diagnosing mismatches).  But

> I'm sure the tests for these things could stand to beefed up so there

> could easily be cases that aren't handled correctly.

> 

>>

>>>> I think you want to move the def_p/idxdef/idxguide logic into 

>>>> another member function that you invoke on cdlguide to perhaps get 

>>>> the class_key_loc_t that you want to use as the pattern.

>>>

>>> I'm not quite sure what you have in mind here.  I agree the cdlcode

>>> code looks a little cumbersome and perhaps could be restructured but

>>> it's not obvious to me how.  Nothing I tried looked like a clear win

>>> so unless you consider changing how this is done a prerequisite for

>>> accepting the whole patch I'd rather not spend any more time at this

>>> stage iterating over refinements of it.  Please let me know soon.

>>

>> I mean that

>>

>>> +  const unsigned ndecls = locvec.length (); > +  const bool def_p = 

>>> idxdef < ndecls;

>>> +  const unsigned idxguide = def_p ? idxdef : 0;

>>

>> are based on whether the instantiation, rather than the template, is 

>> defined.

>>

>> I's probably enough to update ndecls to cdlguide->locvec.length() and 

>> change the uses of idxdef to cdlguide->idxdef.  And then idxguide will 

>> be set properly for cdlguide, so the code farther above can become

>>

>>> +  const tag_types xpect_key

>>> +    = cdlguide->class_key (idxguide);

>>

>> If you'd prefer, I can make these changes and commit the patch myself.

> 

> Please go ahead.  That will make it a lot quicker.


Done.  Here are the changes I made.

Jason
commit 882a90dbfdd776ce33749ae4a44dc89588417a63
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Mar 27 09:56:13 2020 -0400

    set def_p and idxguide based on cdlguide

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 2f539570aae..3ca8eb9baf8 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -31024,6 +31024,12 @@ private:
     return locvec[i].class_key;
   }
 
+  /* True if a definition for the class has been seen.  */
+  bool def_p () const
+  {
+    return idxdef < locvec.length ();
+  }
+
   /* The location of a single mention of a class type with the given
      class-key.  */
   struct class_key_loc_t
@@ -31273,11 +31279,6 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 
   /* Number of uses of the class.  */
   const unsigned ndecls = locvec.length ();
-  if (ndecls < 2)
-    return;
-
-  /* Set if a definition for the class has been seen.  */
-  const bool def_p = idxdef < ndecls;
 
   /* The class (or template) declaration guiding the decisions about
      the diagnostic.  For ordinary classes it's the same as THIS.  For
@@ -31305,17 +31306,13 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 	return;
     }
 
+  /* Set if a definition for the class has been seen.  */
+  const bool def_p = cdlguide->def_p ();
+
   /* The index of the declaration whose class-key this declaration
      is expected to match.  It's either the class-key of the class
      definition if one exists or the first declaration otherwise.  */
-  const unsigned idxguide = def_p ? idxdef : 0;
-  unsigned idx = 0;
-  /* Advance IDX to the first declaration that either is not
-     a definition or that doesn't match the first declaration
-     if no definition is provided.  */
-  while (class_key (idx) == cdlguide->class_key (idxguide))
-    if (++idx == ndecls)
-      return;
+  const unsigned idxguide = def_p ? cdlguide->idxdef : 0;
 
   /* The class-key the class is expected to be declared with: it's
      either the key used in its definition or the first declaration
@@ -31323,10 +31320,15 @@ class_decl_loc_t::diag_mismatched_tags (tree type_decl)
      For implicit instantiations of a primary template it's
      the class-key used to declare the primary with.  The primary
      must be at index zero.  */
-  const tag_types xpect_key
-    = cdlguide->class_key (cdlguide == this ? idxguide : 0);
-  if (cdlguide != this && xpect_key == class_key (idx))
-    return;
+  const tag_types xpect_key = cdlguide->class_key (idxguide);
+
+  unsigned idx = 0;
+  /* Advance IDX to the first declaration that either is not
+     a definition or that doesn't match the first declaration
+     if no definition is provided.  */
+  while (class_key (idx) == xpect_key)
+    if (++idx == ndecls)
+      return;
 
   /* Save the current function before changing it below.  */
   tree save_func = current_function_decl;

Patch

PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
PR c++/93810 - missing -Wmismatched-tags and -Wredundant-tags on a typedef of an implicit class template specialization

gcc/cp/ChangeLog:

	PR c++/93824
	PR c++/93810
	* parser.c (cp_parser_check_class_key): Move code...
	(class_decl_loc_t::add): ...to here.  Add parameters.  Avoid issuing
	-Wredundant-tags on first-time declarations in other declarators.
	(class_decl_loc_t::diag_mismatched_tags): Also expect to be called
	when -Wredundant-tags is enabled.

gcc/testsuite/ChangeLog:

	PR c++/93824
	PR c++/93810
	* g++.dg/warn/Wmismatched-tags-3.C: New test.
	* g++.dg/warn/Wredundant-tags-3.C: Remove xfails.
	* g++.dg/warn/Wredundant-tags-6.C: New test.

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index ca85d899427..3cf2f2d8b34 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -30924,8 +30924,9 @@  class class_decl_loc_t
   /* Issues -Wmismatched-tags for all classes.  */
   static void diag_mismatched_tags ();
 
-  /* Adds TYPE_DECL to the collection of class decls.  */
-  static void add (tree, tag_types, bool, bool);
+  /* Adds TYPE_DECL to the collection of class decls and diagnoses
+     redundant tags (if -Wredundant-tags is enabled).  */
+  static void add (cp_parser *, location_t, tag_types, tree, bool, bool);
 
   /* Either adds this decl to the collection of class decls
      or diagnoses it, whichever is appropriate.  */
@@ -31021,6 +31022,19 @@  cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
       && class_key != union_type)
     return;
 
+  class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p);
+}
+
+/* Adds TYPE to the collection of class decls and diagnoses redundant
+   tags (if -Wredundant-tags is enabled).
+   DEF_P is expected to be set for a definition of class TYPE.  DECL_P
+   is set for a declaration of class TYPE and clear for a reference to
+   it that is not a declaration of it.  */
+
+void
+class_decl_loc_t::add (cp_parser *parser, location_t key_loc,
+		       tag_types class_key, tree type, bool def_p, bool decl_p)
+{
   tree type_decl = TYPE_MAIN_DECL (type);
   tree name = DECL_NAME (type_decl);
   /* Look up the NAME to see if it unambiguously refers to the TYPE
@@ -31032,7 +31046,10 @@  cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
   /* The class-key is redundant for uses of the CLASS_TYPE that are
      neither definitions of it nor declarations, and for which name
      lookup returns just the type itself.  */
-  bool key_redundant = !def_p && !decl_p && decl == type_decl;
+  bool key_redundant = (!def_p && !decl_p
+			&& (decl == type_decl
+			    || TREE_CODE (decl) == TEMPLATE_DECL
+			    || TYPE_BEING_DEFINED (type)));
 
   if (key_redundant
       && class_key != class_type
@@ -31050,29 +31067,8 @@  cp_parser_check_class_key (cp_parser *parser, location_t key_loc,
 	key_redundant = false;
     }
 
-  if (key_redundant)
-    {
-      gcc_rich_location richloc (key_loc);
-      richloc.add_fixit_remove (key_loc);
-      warning_at (&richloc, OPT_Wredundant_tags,
-		  "redundant class-key %qs in reference to %q#T",
-		  class_key == union_type ? "union"
-		  : class_key == record_type ? "struct" : "class",
-		  type);
-    }
-
-  if (seen_as_union || !warn_mismatched_tags)
-    return;
-
-  class_decl_loc_t::add (type_decl, class_key, key_redundant, def_p);
-}
-
-/* Adds TYPE_DECL to the collection of class decls.  */
-
-void
-class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
-		       bool def_p)
-{
+  /* Set if a declaration of TYPE has previously been seen or if it
+     must exist in a precompiled header.  */
   bool exist;
   class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist);
   if (!exist)
@@ -31082,30 +31078,51 @@  class_decl_loc_t::add (tree type_decl, tag_types class_key, bool redundant,
 	{
 	  /* TYPE_DECL is the first declaration or definition of the type
 	     (outside precompiled headers -- see below).  Just create
-	     a new entry for it.  */
+	     a new entry for it and return unless it's a declaration
+	     involving a template that may need to be diagnosed by
+	     -Wredundant-tags.  */
 	  *rdl = class_decl_loc_t (class_key, false, def_p);
-	  return;
+	  if (TREE_CODE (decl) != TEMPLATE_DECL)
+	    return;
+	}
+      else
+	{
+	  /* TYPE was previously defined in some unknown precompiled hdeader.
+	     Simply add a record of its definition at an unknown location and
+	     proceed below to add a reference to it at the current location.
+	     (Declarations in precompiled headers that are not definitions
+	     are ignored.)  */
+	  tag_types def_key
+	    = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
+	  location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
+	  *rdl = class_decl_loc_t (def_key, false, true, def_loc);
+	  exist = true;
 	}
-
-      /* TYPE was previously defined in some unknown precompiled hdeader.
-	 Simply add a record of its definition at an unknown location and
-	 proceed below to add a reference to it at the current location.
-	 (Declarations in precompiled headers that are not definitions
-	 are ignored.)  */
-      tag_types def_key
-	= CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type;
-      location_t def_loc = DECL_SOURCE_LOCATION (type_decl);
-      *rdl = class_decl_loc_t (def_key, false, true, def_loc);
     }
 
   /* A prior declaration of TYPE_DECL has been seen.  */
 
+  if (key_redundant)
+    {
+      gcc_rich_location richloc (key_loc);
+      richloc.add_fixit_remove (key_loc);
+      warning_at (&richloc, OPT_Wredundant_tags,
+		  "redundant class-key %qs in reference to %q#T",
+		  class_key == union_type ? "union"
+		  : class_key == record_type ? "struct" : "class",
+		  type);
+    }
+
+  if (!exist)
+    /* Do nothing if this is the first declaration of the type.  */
+    return;
+
   if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key)
     /* Do nothing if the class-key in this declaration matches
        the definition.  */
     return;
 
-  rdl->add_or_diag_mismatched_tag (type_decl, class_key, redundant, def_p);
+  rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant, def_p);
 }
 
 /* Either adds this DECL corresponding to the TYPE_DECL to the collection
@@ -31158,6 +31175,9 @@  class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl,
 void
 class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 {
+  if (!warn_mismatched_tags)
+    return;
+
   unsigned ndecls = locvec.length ();
 
   /* Skip a declaration that consistently uses the same class-key
@@ -31249,20 +31269,26 @@  class_decl_loc_t::diag_mismatched_tags (tree type_decl)
 void
 class_decl_loc_t::diag_mismatched_tags ()
 {
-  /* CLASS2LOC should be empty if -Wmismatched-tags is disabled.  */
-  gcc_assert (warn_mismatched_tags || class2loc.is_empty ());
+  /* CLASS2LOC should be empty if both -Wmismatched-tags and
+     -Wredundant-tags are disabled.  */
+  gcc_assert (warn_mismatched_tags
+	      || warn_redundant_tags
+	      || class2loc.is_empty ());
 
   /* Save the current function before changing it below.  It should
      be null at this point.  */
   tree save_func = current_function_decl;
 
-  /* Iterate over the collected class/struct declarations.  */
-  typedef class_to_loc_map_t::iterator iter_t;
-  for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+  if (warn_mismatched_tags)
     {
-      tree type_decl = (*it).first;
-      class_decl_loc_t &recloc = (*it).second;
-      recloc.diag_mismatched_tags (type_decl);
+      /* Iterate over the collected class/struct declarations.  */
+      typedef class_to_loc_map_t::iterator iter_t;
+      for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it)
+	{
+	  tree type_decl = (*it).first;
+	  class_decl_loc_t &recloc = (*it).second;
+	  recloc.diag_mismatched_tags (type_decl);
+	}
     }
 
   class2loc.empty ();
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
new file mode 100644
index 00000000000..ecbe66d037e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags-3.C
@@ -0,0 +1,14 @@ 
+/* { dg-do compile }
+   { dg-options "-Wall -Wmismatched-tags" } */
+
+extern class C1 c1;             // { dg-message "declared as 'class'" }
+extern struct C1 c1;            // { dg-warning "\\\[-Wmismatched-tags" }
+
+void fs1 (struct S1);           // { dg-message "declared as 'struct'" }
+void fs1 (class S1);            // { dg-warning "\\\[-Wmismatched-tags" }
+
+enum
+{
+  ec2 = sizeof (struct C2*),    // { dg-message "declared as 'struct'" }
+  fc2 = sizeof (class C2*)      // { dg-warning "\\\[-Wmismatched-tags" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
index 7b30e949d0c..0eeee34dae7 100644
--- a/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-3.C
@@ -34,12 +34,12 @@  union N::U u3;        // { dg-warning "-Wredundant-tags" }
 
 typedef N::TC<0> TC0;
 typedef typename N::TC<0> TC0;
-typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef class N::TC<0> TC0;   // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
 
 typedef N::TS<0> TS0;
 typedef typename N::TS<0> TS0;
-typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" "pr93809" { xfail *-*-*} .-1 }
+typedef struct N::TS<0> TS0;  // { dg-warning "-Wredundant-tags" }
diff --git a/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
new file mode 100644
index 00000000000..a21cc14b2d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wredundant-tags-6.C
@@ -0,0 +1,51 @@ 
+/* PR c++/93824 - bogus -Wredundant-tags on a first declaration in use
+   { dg-do compile }
+   { dg-options "-Wredundant-tags" } */
+
+extern class C1 &c1;              // { dg-bogus "\\\[-Wredundant-tags" }
+extern class C1 &c1;              // { dg-warning "\\\[-Wredundant-tags" }
+
+void fc2 (class C2);              // { dg-bogus "\\\[-Wredundant-tags" }
+void fc2 (class C2);              // { dg-warning "\\\[-Wredundant-tags" }
+
+const int
+npc3 = sizeof (class C3*);        // { dg-bogus "\\\[-Wredundant-tags" }
+const int
+nppc3 = sizeof (class C3**);      // { dg-warning "\\\[-Wredundant-tags" }
+
+extern struct S1 *s1p;            // { dg-bogus "\\\[-Wredundant-tags" }
+extern struct S1 s1a[];           // { dg-warning "\\\[-Wredundant-tags" }
+
+struct S3
+{
+  struct S3 *p1s3;                // { dg-warning "\\\[-Wredundant-tags" }
+  S3 *p2s3;
+
+  union U1 *p1u1;                 // { dg-bogus "\\\[-Wredundant-tags" }
+  union U1 *p2u1;                 // { dg-warning "\\\[-Wredundant-tags" }
+} s3;
+
+typedef struct S3 TS3;            // { dg-warning "\\\[-Wredundant-tags" }
+
+typedef struct S4 TS4;
+
+struct S5
+{
+  struct S6
+  {
+  private:
+    // 'struct' is redundant in a declaration of a pointer to ::S5;
+    struct S5 *ps5;               // { dg-warning "\\\[-Wredundant-tags" }
+    // 'struct' is required in a definition of a new type.
+    struct S5 { } *ps5_2;
+    struct S5 *p2s5_2;            // { dg-warning "\\\[-Wredundant-tags" }
+  };
+};
+
+
+template <class> struct S7;
+template <class> struct S7 { };
+
+template <> struct S7<int>;
+template <> struct S7<int> { };
+struct S7<void> s7v;              // { dg-warning "\\\[-Wredundant-tags" }