From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-qk1-x744.google.com (mail-qk1-x744.google.com [IPv6:2607:f8b0:4864:20::744]) by sourceware.org (Postfix) with ESMTPS id 2BEF33877007 for ; Sat, 21 Mar 2020 21:59:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 2BEF33877007 Received: by mail-qk1-x744.google.com with SMTP id h14so11247632qke.5 for ; Sat, 21 Mar 2020 14:59:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=TGVsTuYkMnDglJQwxnnFkRjzjP3jhuLMxztVm69yUOo=; b=WdQ01CO9g4waMk0sITvjI0pHdH8tI33otrx4mJkew7L1o9dUbVwv5mpJEa9pI0K/6k YKAasACyxlNzafIN7futpunW7fwcZsBWaDWF1EmKpTS2uj16Mff58o0KEDi/T9Ry71Ls QY1OXzKa7/QRx4e0GAadPeD+YtK/S7jqUaEJhZMnwKmxqdzK9UTyPMk59WQ66L3nFlwu 1mBawqiqexmReiXFKC+pRb/yDFZtoAs2OikOFrkcjMwnvcddy89D4I8gLw4q87UzCGSv yA6Fpx9fLo/DZAVXDFYpAu257kEfyCQcAeruI0rYohJ5FoE/oCy8IoV0uaCYTkBJ7v+B dS3w== X-Gm-Message-State: ANhLgQ1NatXV4uwKaNXzJkdLnnhLP8Pzr6/sW1jc7NJVYjuca+Rwtu30 yOFHuRTxtlQL8CRrFjGhMSDYMVSd X-Google-Smtp-Source: ADFU+vvXIPTs6oFz6IQW8zU+iaXXBO/17jKZBHAbPCMdhL6ALgqYTZhvZ1Q2cq+L8tqP0X0bcPYgYw== X-Received: by 2002:a37:a749:: with SMTP id q70mr11803510qke.226.1584827995429; Sat, 21 Mar 2020 14:59:55 -0700 (PDT) Received: from [192.168.0.41] (97-118-124-45.hlrn.qwest.net. [97.118.124.45]) by smtp.gmail.com with ESMTPSA id f21sm8511602qtc.97.2020.03.21.14.59.53 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 21 Mar 2020 14:59:54 -0700 (PDT) Subject: Re: [PATCH] avoid -Wredundant-tags on a first declaration in use (PR 93824) To: Jason Merrill , gcc-patches References: <010f6d68-a64e-45a4-744e-c040a4ea94d6@redhat.com> <65013bfc-23c3-47fb-a58d-9ab802d1febb@gmail.com> <2698a399-4176-2b5f-a134-52a0d82c2121@redhat.com> <13b8faa9-74b1-6131-5dda-d4f34dfa8af0@gmail.com> <77dc94af-40f1-46fd-f3f4-ceb7e3afc4b6@gmail.com> <00f21176-ea92-937c-5c8a-c04d5d813257@redhat.com> <90a6aebc-8fef-0b96-cd2b-3234bd82b9a0@gmail.com> <2c553c0d-c0ac-88ba-2a85-12c523a9a164@redhat.com> <2744364d-e705-06f8-064a-5425ade11669@gmail.com> <1239c5f0-7ea7-e760-0b1c-1fe725a5e129@redhat.com> <48d42e59-64c4-6674-40a4-c786f9e504f4@gmail.com> From: Martin Sebor Message-ID: <6775e196-9f97-6651-bc6a-ee6d4b7d16bc@gmail.com> Date: Sat, 21 Mar 2020 15:59:52 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.6.1 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------9C7B641E295F11E8DD40ED6C" Content-Language: en-US X-Spam-Status: No, score=-31.3 required=5.0 tests=BAYES_00, BODY_8BITS, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GARBLED_BODY, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 21 Mar 2020 22:00:00 -0000 This is a multi-part message in MIME format. --------------9C7B641E295F11E8DD40ED6C Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit 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    struct S; >>    template struct S; >> >>    extern class  S s1;   // expect -Wmismatched-tags >>    extern struct S 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 struct S’ declared with a >> mismatched class-key ‘struct’ [-Wmismatched-tags] >>      5 | extern struct S s2; >>        |               ^~~~~~~ >> z.C:5:15: note: remove the class-key or replace it with ‘class’ >> z.C:4:15: note: ‘template struct S’ first declared as >> ‘class’ here >>      4 | extern class  S 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’ declared with a mismatched class-key > ‘class’ [-Wmismatched-tags] >     4 |   extern class  S s1;   // expect -Wmismatched-tags >       |                 ^~~~~~~ > wa2.C:4:17: note: remove the class-key or replace it with ‘struct’ > wa2.C:2:29: note: ‘S’ first declared as ‘struct’ here >     2 |   template struct S; > > So the only difference is whether we talk about S or S.  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 --------------9C7B641E295F11E8DD40ED6C Content-Type: text/x-patch; name="gcc-93824.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcc-93824.diff" 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 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 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 S1; +template <> struct S1; + +template class S1; +template struct S1; // { dg-warning "\\\[-Wmismatched-tags" } + +template <> class S1; +template <> struct S1; // { dg-warning "\\\[-Wmismatched-tags" } + +template <> class S1; // { dg-warning "\\\[-Wmismatched-tags" } +template <> struct S1; + +extern S1 s1v; +extern class S1 s1v; +extern struct S1 s1v; // { dg-warning "\\\[-Wmismatched-tags" } + +extern S1 s1i; +extern class S1 s1i; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S1 s1i; + +extern S1 s1c; +extern class S1 s1c; +extern struct S1 s1c; // { dg-warning "\\\[-Wmismatched-tags" } + + +// Exercise partial specialization. +template struct S2; +template class S2; + +template class S2; // { dg-warning "\\\[-Wmismatched-tags" } +template struct S2; + +template class S2; +template struct S2;// { dg-warning "\\\[-Wmismatched-tags" } + +extern S2 s2i; +extern class S2 s2i; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S2 s2i; + +extern S2 s2ci; +extern class S2 s2ci; +extern struct S2 s2ci; // { dg-warning "\\\[-Wmismatched-tags" } + + +template struct S3; +template class S3; +template struct S3; + +template class S3; // { dg-warning "\\\[-Wmismatched-tags" } +template struct S3; + +template class S3; +template struct S3; // { dg-warning "\\\[-Wmismatched-tags" } + +template class S3; // { dg-warning "\\\[-Wmismatched-tags" } +template struct S3; + +extern S3 s3i; +extern class S3 s3i; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3i; + +extern S3 s3p; +extern class S3 s3p; +extern struct S3 s3p; // { dg-warning "\\\[-Wmismatched-tags" } + +extern S3 s3r; +extern class S3 s3r; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3r; + +// Repeat exactly the same as above. +extern S3 s3i; +extern class S3 s3i; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3i; + +extern S3 s3p; +extern class S3 s3p; +extern struct S3 s3p; // { dg-warning "\\\[-Wmismatched-tags" } + +extern S3 s3r; +extern class S3 s3r; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3r; + +// Repeat the same as above just with different type. +extern S3 s3l; +extern class S3 s3l; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3l; + +extern S3 s3lp; +extern class S3 s3lp; +extern struct S3 s3lp; // { dg-warning "\\\[-Wmismatched-tags" } + +extern S3 s3lr; +extern class S3 s3lr; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct S3 s3lr; + +// Repeat with the class-keys swapped. +extern S3 s3l; +extern struct S3 s3l; +extern class S3 s3l; // { dg-warning "\\\[-Wmismatched-tags" } + +extern S3 s3lp; +extern struct S3 s3lp; // { dg-warning "\\\[-Wmismatched-tags" } +extern class S3 s3lp; + +extern S3 s3lr; +extern struct S3 s3lr; +extern class S3 s3lr; // { dg-warning "\\\[-Wmismatched-tags" } + + +namespace N +{ +template struct A; + +extern class A ai; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct A ai; + +typedef class A AI; // { dg-warning "\\\[-Wmismatched-tags" } +typedef struct A AI; + +template struct B; +template <> class B; +template <> struct B; + +extern class B bi; +extern struct B bi; // { dg-warning "\\\[-Wmismatched-tags" } + +extern class B bc; // { dg-warning "\\\[-Wmismatched-tags" } +extern struct B bc; + +typedef class B BC; // { dg-warning "\\\[-Wmismatched-tags" } +typedef struct B 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 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 struct CN; // { dg-message "declared as 'struct'" } + +template +struct X_CNp1 { + typedef CN CNp1; +}; + +template +struct X_class_CNp1 { + typedef class CN CNp1; // { dg-warning "-Wmismatched-tags" } +}; + +template +struct X_struct_CNp1 { + typedef struct CN CNp1; +}; + + +// Exercise partial specialization of templates with member types. +template class CT1; +template struct CT1 { }; +template struct CT1 { }; +template class CT1 { }; + +template struct CT2; +template struct CT2 { + // Expect class-key to match the primary. + CT1 ct1_0; + class CT1 ct1_1; + struct CT1 ct1_2; // { dg-warning "-Wmismatched-tags" } + + // Expect class-key to match the CT1 partial specialization. + CT1 ct1p1_0; + class CT1 ct1p1_1; // { dg-warning "-Wmismatched-tags" } + struct CT1 ct1p1_2; + + // Expect class-key to match the CT1 partial specialization. + CT1 ct1p2_0; + class CT1 ct1p2_1; // { dg-warning "-Wmismatched-tags" } + struct CT1 ct1p2_2; + + // Expect class-key to match the CT1 partial specialization. + CT1 ct1p3_0; + class CT1 ct1p3_1; + struct CT1 ct1p3_2; // { dg-warning "-Wmismatched-tags" } + + // Expect class-key to still match the CT1 partial specialization. + CT1 ct1p4_0; + class CT1 ct1p4_1; + struct CT1 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 D; +template struct D; +template class D; +template struct D; +template class D; +template struct D; +template class D; +template struct D; +template class D; + +typedef class D DIP; // { dg-warning "-Wmismatched-tags" } +typedef struct D DIP; +typedef class D DIP; // { dg-warning "-Wmismatched-tags" } +typedef struct D DIP; + +typedef class D DIR; +typedef struct D DIR; // { dg-warning "-Wmismatched-tags" } +typedef class D DIR; + + +typedef struct D DCIP; +typedef class D DCIP; // { dg-warning "-Wmismatched-tags" } +typedef struct D DCIP; + +typedef struct D DCIR; // { dg-warning "-Wmismatched-tags" } +typedef class D DCIR; +typedef struct D DCIR; // { dg-warning "-Wmismatched-tags" } + + +typedef struct D DVIP; +typedef class D DVIP; // { dg-warning "-Wmismatched-tags" } +typedef struct D DVIP; + +typedef struct D DVIR; // { dg-warning "-Wmismatched-tags" } +typedef class D DVIR; +typedef struct D DVIR; // { dg-warning "-Wmismatched-tags" } + + +typedef struct D DCVIP; +typedef class D DCVIP; // { dg-warning "-Wmismatched-tags" } +typedef struct D DCVIP; + +typedef struct D DCVIR; // { dg-warning "-Wmismatched-tags" } +typedef class D DCVIR; +typedef struct D 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 B; // { dg-message "declared as 'class'" } + +using Bi = B; +using Bi = class B; +using Bi = struct B; // { dg-warning "-Wmismatched-tags" } +using Bi = class B; +using Bi = struct B; // { dg-warning "-Wmismatched-tags" } + + +template class C; // { dg-message "declared as 'class'" } + +template using Cp = C; +template using Cp = class C; +template +using Cp = struct C; // { dg-warning "-Wmismatched-tags" } + +template using Cp = class C; +template +using Cp = struct C; // { 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 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 struct TS2; +template struct TS2; +template struct TS2; + +template +struct TS4 +{ + typedef struct TS2 TS2_CT1; // { dg-warning "\\\[-Wredundant-tags" } + typedef TS2 TS2_CT2; + + typedef struct TS2 TS2_T1; // { dg-warning "\\\[-Wredundant-tags" } + typedef TS2 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 B; + +using Bi = B; +using Bi = class B; // { dg-warning "-Wredundant-tags" } +using Bi = struct B; // { dg-warning "-Wredundant-tags" } + + +template class C; + +template +using Cp = C; +template +using Cp = class C; // { dg-warning "-Wredundant-tags" } +template +using Cp = struct C; // { dg-warning "-Wredundant-tags" } --------------9C7B641E295F11E8DD40ED6C--