From patchwork Mon Jul 8 21:58:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 1129377 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-504655-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="hxBCB+OZ"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="uovJ4tEt"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45jK8G6QrYz9sMr for ; Tue, 9 Jul 2019 07:58:22 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:from:to:references:message-id:date:mime-version :in-reply-to:content-type; q=dns; s=default; b=So8gOZf3tYuRUynix sn/qh/4ndQMrv+DPzlBdQ7dFxzIGChynEEQbHfttC2hxUnn/4LhQq8nA3RMwjP1U /kSFvaqwg/2ryufgogdMgkyBRbRUCtzQsx7uGpGkoXV4p7qRp5bTI+bGQ00Ed5mF yM+nN97tNCzdhgAALVrcAJCayE= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:from:to:references:message-id:date:mime-version :in-reply-to:content-type; s=default; bh=zbQVffC/ojnDto/d1OtAldX VfcU=; b=hxBCB+OZ2Q5E09YUPxZZmAQoBiaemGzx+kkNvsxzm/CBC2cUXtTmqnW PyfQN8NqzEO3YY2LyAqxu+qHawgvmJiTZxea+KYoYW2C6G2hJWcIlxdneUgwLMXT 994MHLLy0p/6DW9969XB6cQJ1XOshiiNLEXJsxD9ZWS/IF5vqGtQ= Received: (qmail 11364 invoked by alias); 8 Jul 2019 21:58:12 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 11356 invoked by uid 89); 8 Jul 2019 21:58:11 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.2 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy=primary, proxy, 2628 X-HELO: mail-qk1-f179.google.com Received: from mail-qk1-f179.google.com (HELO mail-qk1-f179.google.com) (209.85.222.179) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 08 Jul 2019 21:58:06 +0000 Received: by mail-qk1-f179.google.com with SMTP id v22so14525207qkj.8 for ; Mon, 08 Jul 2019 14:58:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:from:to:references:message-id:date:user-agent:mime-version :in-reply-to:content-language; bh=HhU1wV7kRY3sZt1JrlEhXDRJFbTTWcRvsGnxlWH1ICE=; b=uovJ4tEtYzmlNj6HaxuZD2U+K/FImjGPo8SRJ+p16jj0leZ/J1iyW7t8SoBH6NiOVa vgofcN7h1U4D3fZ9jLjyQy2IafzL2EbdeYSgOKec0i5EnHSGDZDKvNEqnM3P4JM1e6iK SJinszW0mlKVDz+t8DFQAxla/D5KcOzjy3H3miQgPsgEWbfotNMUoMKbyf9gaLi5zHY9 UWuu3vsqpr9pb/lqtV6TKK9klyPn2lH3Mm263TLFnQJ4DZ4fjcHHBDfBhWZDiaY5Ns9s hg8WW3QQNt8tj/9xVmlLle1MeImnDShIAp/R5FAt3epj8HTqPKbiiuJXLMr3IownjZSH q7Hw== Received: from [192.168.0.41] (174-16-116-9.hlrn.qwest.net. [174.16.116.9]) by smtp.gmail.com with ESMTPSA id x10sm11197653qtc.34.2019.07.08.14.58.03 for (version=TLS1_3 cipher=AEAD-AES128-GCM-SHA256 bits=128/128); Mon, 08 Jul 2019 14:58:03 -0700 (PDT) Subject: [PATCH 1/3] add -Wstruct-not-pod, -Wclass-is-pod, -Wmismatched-tags (PR 61339) From: Martin Sebor To: gcc-patches References: Message-ID: Date: Mon, 8 Jul 2019 15:58:02 -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: X-IsSubscribed: yes The attached patch implements three new warnings: * -Wstruct-not-pod triggers for struct definitions that are not POD structs, * -Wclass-is-pod triggers for class definitions that satisfy the requirements on POD structs, and * -Wmismatched-tags that triggers for class and struct declarations with class-key that doesn't match either their definition or the first declaration (if no definition is provided). The implementation of -Wclass-is-pod and -Wstruct-not-pod is fairly straightforward but the -Wmismatched-tags solution is slightly unusual. It collects struct and class declarations first and diagnoses mismatches only after the whole tramslation unit has been processed. This is so that the definition of a class can guide which declarations to diagnose no matter which come first. Martin gcc/c-family/ChangeLog: * c.opt (-Wstruct-not-pod, -Wclass-is-pod): New options. (-Wmismatched-tags): Same. gcc/cp/ChangeLog: * parser.c (maybe_warn_struct_vs_class): New function. (cp_parser_check_class_key): Add argument. (cp_parser_type_specifier): Call maybe_warn_struct_vs_class. (cp_parser_elaborated_type_specifier): Call maybe_warn_struct_vs_class before setting CLASSTYPE_DECLARED_CLASS. Avoid setting it for classes that are in the process of being defined. (cp_parser_class_head): Call maybe_warn_struct_vs_class. (class_or_template_pod_p): New static function. (maybe_warn_struct_vs_class) Same. (class rec_decl_loc_t): New. (cp_parser_check_class_key): Record a struct declaration. (diag_mismatched_tags): Hanlde -Wmismatched-tags. (c_parse_file): Call diag_mismatched_tags. gcc/ChangeLog: * doc/invoke.texi (-Wstruct-not-pod, -Wclass-is-pod): Document new options. (-Wmismatched-tags): Same. diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 080066fa608..0bc23a6f409 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -736,6 +736,10 @@ Wmisleading-indentation C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall) Warn when the indentation of the code does not reflect the block structure. +Wmismatched-tags +C++ Objc++ Var(warn_mismatched_tags) Warning +Warn when a class is redeclared using a mismatched class-key. + Wmissing-braces C ObjC C++ ObjC++ Var(warn_missing_braces) Warning LangEnabledBy(C ObjC,Wall) Warn about possibly missing braces around initializers. @@ -794,6 +798,14 @@ Wstringop-truncation C ObjC C++ LTO ObjC++ Var(warn_stringop_truncation) Warning Init (1) LangEnabledBy(C ObjC C++ LTO ObjC++, Wall) Warn about truncation in string manipulation functions like strncat and strncpy. +Wstruct-not-pod +C++ ObjC++ Var(warn_struct_not_pod) Warning +Warn about structs that are not POD. + +Wclass-is-pod +C++ ObjC++ Var(warn_class_is_pod) Warning +Warn about classes that are POD. + Wsuggest-attribute=format C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning Warn about functions which might be candidates for format attributes. diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 12814102465..74accff2c6d 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -262,6 +262,8 @@ static bool cp_parser_omp_declare_reduction_exprs static void cp_finalize_oacc_routine (cp_parser *, tree, bool); +static void maybe_warn_struct_vs_class (location_t, tree); + /* Manifest constants. */ #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) #define CP_SAVED_TOKEN_STACK 5 @@ -2580,7 +2582,7 @@ static enum tag_types cp_parser_token_is_class_key static enum tag_types cp_parser_token_is_type_parameter_key (cp_token *); static void cp_parser_check_class_key - (enum tag_types, tree type); +(cp_parser *, enum tag_types, tree type, bool); static void cp_parser_check_access_in_redeclaration (tree type, location_t location); static bool cp_parser_optional_template_keyword @@ -17442,6 +17444,8 @@ cp_parser_type_specifier (cp_parser* parser, type_spec, token, /*type_definition_p=*/true); + + maybe_warn_struct_vs_class (token->location, type_spec); return type_spec; } @@ -18621,11 +18625,13 @@ cp_parser_elaborated_type_specifier (cp_parser* parser, if (tag_type != enum_type) { + /* Diagnose class/struct/union mismatches. */ + cp_parser_check_class_key (parser, tag_type, type, false); + /* Indicate whether this class was declared as a `class' or as a `struct'. */ - if (CLASS_TYPE_P (type)) + if (CLASS_TYPE_P (type) && !currently_open_class (type)) CLASSTYPE_DECLARED_CLASS (type) = (tag_type == class_type); - cp_parser_check_class_key (tag_type, type); } /* A "<" cannot follow an elaborated type specifier. If that @@ -24168,11 +24174,13 @@ cp_parser_class_head (cp_parser* parser, parser->num_template_parameter_lists); } + /* Diagnose class/struct/union mismatches. */ + cp_parser_check_class_key (parser, class_key, type, true); + /* Indicate whether this class was declared as a `class' or as a `struct'. */ if (TREE_CODE (type) == RECORD_TYPE) - CLASSTYPE_DECLARED_CLASS (type) = (class_key == class_type); - cp_parser_check_class_key (class_key, type); + CLASSTYPE_DECLARED_CLASS (type) = class_key == class_type; /* If this type was already complete, and we see another definition, that's an error. Likewise if the type is already being defined: @@ -28039,6 +28047,127 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, return fn; } +/* Return true if the class or class template TYPE appears to meet + the requirements of a POD type even if some of the instantiations + of the latter may not. The result is not exactly the same as + that of a call to pod_type_p. */ + +static bool +class_or_template_pod_p (tree type) +{ + if (type == error_mark_node + || TYPE_HAS_USER_CONSTRUCTOR (type) + || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) + || (TYPE_LANG_SPECIFIC (type) + && TYPE_HAS_COPY_ASSIGN (type) + && (cxx_dialect != cxx98 + || (TYPE_LANG_SPECIFIC (type) + && !TYPE_HAS_TRIVIAL_COPY_ASSIGN (type))))) + return false; + + /* Stores the last access specifier. */ + tree last_access = NULL_TREE; + + for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + { + if (DECL_ARTIFICIAL (fld)) + continue; + + if (TREE_CODE (fld) == FIELD_DECL + && !TREE_STATIC (fld) + && TREE_TYPE (fld)) + { + tree fldtype = TREE_TYPE (fld); + if (fldtype == error_mark_node) + return false; + + if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (fldtype)) + continue; + + if (TREE_CODE (fldtype) == REFERENCE_TYPE) + return false; + + tree access = declared_access (fld); + if (last_access && access != last_access) + return false; + last_access = access; + + if (TREE_CODE (fldtype) == RECORD_TYPE + && !class_or_template_pod_p (fldtype)) + return false; + } + else if (TREE_CODE (fld) == FUNCTION_DECL + && DECL_NONSTATIC_MEMBER_FUNCTION_P (fld) + && DECL_VIRTUAL_P (fld)) + return false; + } + + return true; +} + +/* For a DECL of class type, issue a warning when it is a POD type + and is declared with the class-key class, or when it is not a POD + type and is declared withe the class-key struct. When DECL refers + to a class template, consider instead whether it has a ctor, dtor, + or copy assignment operator as a proxy. */ + +static void +maybe_warn_struct_vs_class (location_t loc, tree type) +{ + if (TREE_CODE (type) != RECORD_TYPE) + return; + + bool warned = false; + const char *key = class_key_or_enum_as_string (type); + if (processing_template_decl) + { + if (class_or_template_pod_p (type)) + { + if (!strcmp (key, "class")) + warned = warning_at (loc, OPT_Wclass_is_pod, + "POD-like template %qT declared with " + "class-key %qs; use %qs instead", + type, key, "struct"); + } + else if (strcmp (key, "class")) + warned = warning_at (loc, OPT_Wstruct_not_pod, + "non-POD-like template %qT declared with " + "class-key %qs; use %qs instead", + type, key, "class"); + } + else + { + if (pod_type_p (type)) + { + if (!strcmp (key, "class")) + warned = warning_at (loc, OPT_Wclass_is_pod, + "POD type %qT declared with class-key %qs; " + "use %qs instead", + type, key, "struct"); + } + /* In C++ 98 mode call class_or_template_pod_p to get the same + result as in later modes. There is little point in enforcing + consistency with the more restrictive rules. */ + else if (cxx_dialect == cxx98 && class_or_template_pod_p (type)) + { + if (!strcmp (key, "class")) + warned = warning_at (loc, OPT_Wclass_is_pod, + "C++11 POD type %qT declared with " + "class-key %qs; use %qs instead", + type, key, "struct"); + } + else if (strcmp (key, "class")) + warned = warning_at (loc, OPT_Wstruct_not_pod, + "non-POD type %qT declared with class-key %qs; " + "use %qs instead", + type, key, "class"); + } + + if (warned) + inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)), + "previous declaration here"); +} + /* Parse a template-declaration body (following argument list). */ static void @@ -29915,14 +30044,107 @@ cp_parser_token_is_type_parameter_key (cp_token* token) } } -/* Issue an error message if the CLASS_KEY does not match the TYPE. */ +/* Describes the set of declarations of a struct, class, or class template + or its specializations. Used for -Wmismatched-tags. */ + +class rec_decl_loc_t +{ + public: + + rec_decl_loc_t () + : locvec (), idxdef (), def_class_key () + { + locvec.create (4); + } + + /* Construct an object for a single declaration of a class with + CLASS_KEY at the current location in the current function (or + at another scope). KEY_REDUNDANT is true if the class-key may + be omitted in the current context without a change in semantics. + DEF_P is true for a declaration that is a definition. */ + rec_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p) + : locvec (), idxdef (def_p ? 0 : UINT_MAX), + def_class_key (class_key) + { + locvec.create (4); + func_loc_t func_loc (current_function_decl, input_location); + flag_func_loc_t flag_func_loc (key_redundant, func_loc); + locvec.quick_push (class_key_loc_t (class_key, flag_func_loc)); + } + + /* Copy, assign, and destroy the object. Necessary because LOCVEC + isn't safely copyable and assignable and doesn't release storage + on its own. */ + rec_decl_loc_t (const rec_decl_loc_t &rhs) + : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef), + def_class_key (rhs.def_class_key) { } + + rec_decl_loc_t& operator= (const rec_decl_loc_t &rhs) + { + locvec.release (); + locvec = rhs.locvec.copy (); + idxdef = rhs.idxdef; + def_class_key = rhs.def_class_key; + return *this; + } + + ~rec_decl_loc_t () + { + locvec.release (); + } + + tree function (unsigned i) const + { + return locvec[i].second.second.first; + } + + location_t location (unsigned i) const + { + return locvec[i].second.second.second; + } + + bool key_redundant (unsigned i) const + { + return locvec[i].second.first; + } + + tag_types class_key (unsigned i) const + { + return locvec[i].first; + } + + /* The locations of local variables/alloca calls returned by the return + statement. Avoid using auto_vec here since it's not safe to copy due + to pr90904. */ + typedef std::pair func_loc_t; + typedef std::pair flag_func_loc_t; + typedef std::pair class_key_loc_t; + vec locvec; + /* LOCVEC index of the definition or -1 if none exists. */ + unsigned idxdef; + /* The class-key the class was last declared with or none_type when + it has been declared with a mismatched key. */ + tag_types def_class_key; +}; + + +/* A mapping between a TYPE_DECL and the rec_decl_loc_t description + above. */ +typedef hash_map record_to_locs_t; +static record_to_locs_t rec2loc; + +/* Issue an error message if the CLASS_KEY does not match the TYPE. + DEF_P is expected to be set for a definition. */ static void -cp_parser_check_class_key (enum tag_types class_key, tree type) +cp_parser_check_class_key (cp_parser *parser, enum tag_types class_key, + tree type, bool def_p) { if (type == error_mark_node) return; - if ((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type)) + + bool seen_as_union = TREE_CODE (type) == UNION_TYPE; + if (seen_as_union != (class_key == union_type)) { if (permerror (input_location, "%qs tag used in naming %q#T", class_key == union_type ? "union" @@ -29930,7 +30152,132 @@ cp_parser_check_class_key (enum tag_types class_key, tree type) type)) inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)), "%q#T was previously declared here", type); + return; + } + + if (seen_as_union || !warn_mismatched_tags) + return; + + 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 + and set KEY_REDUNDANT if so. */ + tree decl = cp_parser_lookup_name_simple (parser, name, input_location); + bool key_redundant = decl == type_decl; + + if (rec_decl_loc_t *rdl = rec2loc.get (type_decl)) + { + /* A declatation for this TYPE has already been seen earlier. + Reset the class_key associated with this type on mismatch. + This is an optimization that lets the diagnostic code skip + over classes that use the same class-key in all declarations. */ + if (rdl->def_class_key != class_key) + rdl->def_class_key = none_type; + + /* Set IDXDEF to the index of the vector corresponding to + the definition. */ + if (def_p) + rdl->idxdef = rdl->locvec.length (); + typedef rec_decl_loc_t::func_loc_t func_loc_t; + typedef rec_decl_loc_t::flag_func_loc_t flag_func_loc_t; + typedef rec_decl_loc_t::class_key_loc_t class_key_loc_t; + + /* Append a record of this declaration to the vector. */ + rdl->locvec.safe_push (class_key_loc_t (class_key, + flag_func_loc_t (key_redundant, + func_loc_t (current_function_decl, input_location)))); } + else + /* Create a new entry for this (new) declaration. */ + rec2loc.put (type_decl, rec_decl_loc_t (class_key, key_redundant, def_p)); +} + +/* Issue -Wmismatched-tags. */ + +static void +diag_mismatched_tags () +{ + /* REC2LOC should be empty if -Wmismatched-tags is disabled. */ + gcc_assert (warn_mismatched_tags || rec2loc.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 record_to_locs_t::iterator iter_t; + for (iter_t it = rec2loc.begin (); it != rec2loc.end (); ++it) + { + const rec_decl_loc_t &recloc = (*it).second; + unsigned ndecls = recloc.locvec.length (); + + /* Skip those that consistently use the same class-key. */ + if (recloc.def_class_key != none_type || ndecls < 2) + continue; + + tree type_decl = (*it).first; + bool def_p = recloc.idxdef < ndecls; + unsigned idxguide = def_p ? recloc.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 (recloc.class_key (idx) == recloc.class_key (idxguide)) + ++idx; + + /* 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 expect_class_key = recloc.class_key (def_p ? idxguide : 0); + const char *keystr = expect_class_key == record_type ? "struct" : "class"; + /* Set the function declaration to print in diagnostic context. */ + current_function_decl = recloc.function (idx); + auto_diagnostic_group d; + /* Issue a warning for the first mismatched declaration. + Avoid using "%#qT" since the class-key for the same type will + be the same regardless of which one was used in the declaraion. */ + warning_at (recloc.location (idx), OPT_Wmismatched_tags, + "%<%s %T%> declared with a mismatched class-key", + keystr, type_decl); + + /* Mention the first declaration or definition that guided + the decision to issue the warning above. */ + tag_types class_key = recloc.class_key (idxguide); + inform (recloc.location (idxguide), + (def_p + ? G_("%qT defined as %qs here") + : G_("%qT first declared as %qs here")), + type_decl, class_key == record_type ? "struct" : "class"); + + /* Issue warnings for the remaining insistent declarations. */ + for (unsigned i = idx + 1; i != ndecls; ++i) + { + class_key = recloc.class_key (i); + /* Skip over the declarations that match either the definition + if one was provided or the first declaration. */ + if (class_key == expect_class_key) + continue; + + location_t loc = recloc.location (i); + bool key_redundant = recloc.key_redundant (i); + /* Set the function declaration to print in diagnostic context. */ + current_function_decl = recloc.function (i); + warning_at (loc, OPT_Wmismatched_tags, + "%<%s %T%> declared with a mismatched class-key", + keystr, type_decl); + /* Suggest how to avoid the warning for each instance since + the guidance may be different depending on context. */ + inform (loc, + (key_redundant + ? G_("remove the class-key or replace it with %qs") + : G_("replace the class-key with %qs")), + keystr); + } + } + + rec2loc.empty (); + /* Restore the current function. */ + current_function_decl = save_func; } /* Issue an error message if DECL is redeclared with different @@ -41493,6 +41840,8 @@ c_parse_file (void) push_deferring_access_checks (flag_access_control ? dk_no_deferred : dk_no_check); cp_parser_translation_unit (the_parser); + diag_mismatched_tags (); + the_parser = NULL; finish_translation_unit (); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 9c6050b574b..6e8563401fc 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -230,15 +230,17 @@ in the following sections. -fvisibility-inlines-hidden @gol -fvisibility-ms-compat @gol -fext-numeric-literals @gol --Wabi=@var{n} -Wabi-tag -Wconversion-null -Wctor-dtor-privacy @gol +-Wabi=@var{n} -Wabi-tag -Wclass-is-pod @gol +-Wconversion-null -Wctor-dtor-privacy @gol -Wdelete-non-virtual-dtor -Wdeprecated-copy -Wdeprecated-copy-dtor @gol -Wliteral-suffix @gol +-Wmismatched-tags @gol -Wmultiple-inheritance -Wno-init-list-lifetime @gol -Wnamespaces -Wnarrowing @gol -Wpessimizing-move -Wredundant-move @gol -Wnoexcept -Wnoexcept-type -Wclass-memaccess @gol -Wnon-virtual-dtor -Wreorder -Wregister @gol --Weffc++ -Wstrict-null-sentinel -Wtemplates @gol +-Weffc++ -Wstrict-null-sentinel -Wstruct-is-pod -Wtemplates @gol -Wno-non-template-friend -Wold-style-cast @gol -Woverloaded-virtual -Wno-pmf-conversions @gol -Wno-class-conversion -Wno-terminate @gol @@ -3191,6 +3193,28 @@ void h() @{ f(g); @} In C++14, @code{f} calls @code{f}, but in C++17 it calls @code{f}. +@item -Wclass-is-pod @r{(C++ and Objective-C++ only)} +@opindex Wclass-is-pod +@opindex Wno-class-is-pod +Warn for definitions of classes and class templates with the class-key +@code{class} that satisfy the C++ requirements for a POD (Plain Old Data) +struct. A class template definition is considered to satisfy the requirements +if its instantiation on a POD type does, even if its instantiation on a type +that does not meet such requirements would not. For example, template +@code{Pair} below triggers the warning because it satisfies the requirements +of a POD struct for any POD @code{T} and @code{U}. Defining the template +with the class-key @code{struct} avoids the warning. +@smallexample +template +class Pair @{ +public: + T a; + T b; +@} +@end smallexample +For best results use the @option{-Wclass-is-pod} option in conjunction +with options @option{-Wstruct-not-pod} and @option{-Wmismatched-tags}. + @item -Wclass-memaccess @r{(C++ and Objective-C++ only)} @opindex Wclass-memaccess @opindex Wno-class-memaccess @@ -3384,6 +3408,28 @@ to @code{__null}. Although it is a null pointer constant rather than a null pointer, it is guaranteed to be of the same size as a pointer. But this use is not portable across different compilers. +@item -Wstruct-not-pod @r{(C++ and Objective-C++ only)} +@opindex Wstruct-not-pod +@opindex Wno-struct-pod +Warn for definitions of structs and class templates using the class-key +@code{struct} that do not satisfy the C++ requirements for a POD (Plain Old +Data) struct. A class template definition is considered to satisfy +the requirements if and only if its instantiation on a POD type does. For +example, template @code{Pair} below triggers the warning because it does +not satisfy the requirements of a POD struct for any POD @code{T} and @code{U}. +Defining the template with the class-key @code{class} avoids the warning, as +does removing the default constructor. +@smallexample +template +struct Pair @{ + Pair (): a (), b () @{ @} + T a; + T b; +@} +@end smallexample +For best results use the @option{-Wstruct-not-pod} option in conjunction +with options @option{-Wclass-is-pod} and @option{-Wmismatched-tags}. + @item -Wno-non-template-friend @r{(C++ and Objective-C++ only)} @opindex Wno-non-template-friend @opindex Wnon-template-friend @@ -3455,6 +3501,30 @@ The warning is inactive inside a system header file, such as the STL, so one can still use the STL. One may also instantiate or specialize templates. +@item -Wmismatched-tags @r{(C++ and Objective-C++ only)} +@opindex Wmismatched-tags +@opindex Wno-mismatched-tags +Warn for declarations of structs, classes, and class templates and their +specializations with a class-key that does not match either the definition +or the first declaration if no definition is provided. + +For example, the declaration of @code{struct Object} in the argument list +of @code{draw} triggers the warning. To avoid it, either remove the redundant +class-key @code{struct} or replace it with @code{class} to match its definition. +@smallexample +class Object @{ +public: + virtual ~Object () = 0; +@}; +void draw (struct Object*); +@end smallexample + +It is not wrong to declare a class with the class-key @code{struct} as +the example above shows. The @option{-Wmismatched-tags} option is intended +to help achieve a consistent style of class declarations. It can be used +either on its own or in conjunction with options @option{-Wclass-is-pod} +and @option{-Wstruct-not-pod}. + @item -Wmultiple-inheritance @r{(C++ and Objective-C++ only)} @opindex Wmultiple-inheritance @opindex Wno-multiple-inheritance diff --git a/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C new file mode 100644 index 00000000000..efe0889e429 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C @@ -0,0 +1,127 @@ +// { dg-do compile } +// { dg-options "-Wclass-is-pod" } + +namespace Pod +{ +class A // { dg-warning "POD type 'Pod::A' declared with class-key 'class'; use 'struct' instead" } +{ }; +class B // { dg-warning "\\\[-Wclass-is-pod" } +{ public: int i; }; +class C // { dg-warning "\\\[-Wclass-is-pod" } +{ public: void f (); }; +class D // { dg-warning "\\\[-Wclass-is-pod" } +{ void operator= (int); }; + +#if __cplusplus > 199711L +class E // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : A { }; +class F // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : E { }; +class G // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : private A { }; +#endif +} + + +namespace PodTemplate +{ +template +class A // { dg-warning "\\\[-Wclass-is-pod" } +{ }; +template +class B // { dg-warning "\\\[-Wclass-is-pod" } +{ public: int i; }; +template +class C // { dg-warning "\\\[-Wclass-is-pod" } +{ public: void f (); }; +template +class D // { dg-warning "\\\[-Wclass-is-pod" } +{ void operator= (int); }; + +#if __cplusplus > 199711L +template +class E // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : A { }; +template +class F // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : E { }; +template +class G // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : private A { }; +#endif +} + + +namespace NonPodDueToSpecialFunctions +{ +class A +{ public: A (); }; +class B +{ public: B (int); }; + +class C +{ public: C (C&); }; + +class D +{ public: ~D (); }; + +class E +{ public: void operator= (E&); }; +} + + +namespace NonPodDueToVirtuals +{ +class A +{ public: virtual void f (); }; + +} + + +namespace NonPodDueToNonPodMembers +{ +class A +{ public: int &r; }; + +class B { public: B (); }; + +class C +{ public: B b; }; +} + + +namespace NonPodTemplateDueToNonPodMembers +{ +template +class A +{ public: T &r; }; + +class B { public: B (); }; + +template +class C +{ public: B b; }; +} + + + +namespace NonPodDueToAccess +{ +class A +{ int i; public: int j; }; + +class B +{ int i; protected: int j; }; +} + + +namespace NonPodDueToBases +{ +struct A { }; +struct B { }; +class C: A, B // { dg-bogus "\\\[-Wclass-is-pod" "pr91064" { xfail *-*-* } } +{ }; + +class D: virtual A +{ }; +} diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C new file mode 100644 index 00000000000..1166299ef67 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wmismatched-tags.C @@ -0,0 +1,217 @@ +// Test to verify that -Wmismatched-tags is issued for declarations +// of the same class using different class-ids. +// { dg-do compile } +// { dg-options "-Wmismatched-tags -ftrack-macro-expansion=0" } + +namespace Classes +{ +class A; +class A; + +struct B; +struct B; + +union C; +union C; + +struct D; // { dg-warning "'class Classes::D' declared with a mismatched class-key" } +class D // { dg-message "'Classes::D' defined as 'class' here" } +{ public: D (); }; + +class E; // { dg-warning "'struct Classes::E' declared with a mismatched class-key" } +struct E // { dg-message "'Classes::E' defined as 'struct' here" } +{ int i; }; + +class D; +struct E; + +class D; +struct E; + +struct D; // { dg-warning ".class Classes::D. declared with a mismatched class-key" } + // { dg-message "remove the class-key or replace it with 'class'" "hint" { target *-*-* } .-1 } + +class E; // { dg-warning "'struct Classes::E' declared with a mismatched class-key" } +} + + +namespace GlobalObjects +{ +class A; // { dg-message "'GlobalObjects::A' first declared as 'class' here" } +struct B; // { dg-message "'GlobalObjects::B' first declared as 'struct' here" } + +extern class A a1; +extern class A a2; + +extern struct B b1; +extern struct B b2; + +extern struct A a3; // { dg-warning ".class GlobalObjects::A. declared with a mismatched class-key" } +extern class A a4; + +extern class B b3; // { dg-warning ".struct GlobalObjects::B. declared with a mismatched class-key" } +extern struct B b4; +} + + +namespace LocalObjects +{ +class A; // { dg-message "'LocalObjects::A' first declared as 'class' here" } +struct B; // { dg-message "'LocalObjects::B' first declared as 'struct' here" } + +void f () +{ + class A *a1; + class A *a2; + + struct B *b1; + struct B *b2; + + struct A *a3; // { dg-warning ".class LocalObjects::A. declared with a mismatched class-key" } + class A *a4; + + class B *b3; // { dg-warning ".struct LocalObjects::B. declared with a mismatched class-key" } + struct B *b4; +} +} + + +namespace MemberClasses +{ +struct A { struct B; }; +struct C { struct D; struct D; struct D { }; }; +struct E { class F; class F { }; class F; }; + +struct G { + struct H; // { dg-message "'MemberClasses::G::H' first declared as 'struct' here" } + class H; // { dg-warning "'struct MemberClasses::G::H' declared with a mismatched class-key" } + class I { }; // { dg-message "'MemberClasses::G::I' defined as 'class' here" } + struct I; // { dg-warning "'class MemberClasses::G::I' declared with a mismatched class-key" } +}; +} + + +namespace DataMembers +{ +struct A { struct B *p; }; +struct C { struct D *p; struct D *q; struct D { } d; }; +struct E { class F &r; class F { } f; class F *p; }; + +class G; // { dg-message "'DataMembers::G' first declared as 'class' here" } +struct H; // { dg-message "'DataMembers::H' first declared as 'struct' here" } + +struct I { + struct G *p0; // { dg-warning "'class DataMembers::G' declared with a mismatched class-key" } + class G *p1; + + struct H &r0; + class H &r1; // { dg-warning "'struct DataMembers::H' declared with a mismatched class-key" } + + class J { }; // { dg-message "'DataMembers::I::J' defined as 'class' here" } + struct K { }; // { dg-message "'DataMembers::I::K' defined as 'struct' here" } + + class J j0; + class K k0; // { dg-warning "'struct DataMembers::I::K' declared with a mismatched class-key" } + + struct J j1; // { dg-warning "'class DataMembers::I::J' declared with a mismatched class-key" } + struct K k1; +}; + +} + + +namespace Templates +{ +template class A; +template class A; + +template struct B; +template struct B; + +template union C; +template union C; + +template struct D; // { dg-warning "'class Templates::D< >' declared with a mismatched class-key" } +template +class D // { dg-message "'Templates::D< >' defined as 'class' here" } +{ public: D (); }; + +template class E; // { dg-warning "'struct Templates::E< >' declared with a mismatched class-key" } +template +struct E // { dg-message "'Templates::E< >' defined as 'struct' here" } +{ int i; }; + +template class D; +template struct E; + +template +struct D; // { dg-warning "'class Templates::D< >' declared with a mismatched class-key" } + // { dg-message "replace the class-key with 'class'" "hint" { target *-*-* } .-1 } +} + + +namespace ExplicitSpecializations +{ +template class A; +template <> class A<0>; +template <> struct A<1>; +template <> struct A<1> { }; + +template struct B; +template <> struct B<0>; +template <> class B<1>; +template <> class B<2> { public: B (); }; + +template union C; +template <> union C<0>; + +template class D; +template <> class D<0>; // { dg-warning "'struct ExplicitSpecializations::D<0>' declared with a mismatched class-key " } +template <> +struct D<0> { }; // { dg-message "'ExplicitSpecializations::D<0>' defined as 'struct' here" } + +template struct E; +template <> struct E<0>; // { dg-warning "'class ExplicitSpecializations::E<0>' declared with a mismatched class-key" } +template <> +class E<0> { }; // { dg-message "'ExplicitSpecializations::E<0>' defined as 'class' here" } + +template struct F; +template <> class F<0> { }; // { dg-message "'ExplicitSpecializations::F<0>' defined as 'class' here" } + +template <> +struct F<0>; // { dg-warning "'class ExplicitSpecializations::F<0>' declared with a mismatched class-key" } +} + + +namespace PartialSpecializations +{ +template class A; +template struct A; +template struct A; + +template struct B; +template class B; +template class B; + +template class C { }; +template struct C { }; +template struct C { }; + +template struct D { }; +template class D { }; +template class D { }; + +template class E; +template +struct E; // { dg-message "note: 'PartialSpecializations::E' first declared as 'struct' here" } + +template +class E; // { dg-warning "struct PartialSpecializations::E' declared with a mismatched class-key" } + +template class F; +template +class F; // { dg-message "'PartialSpecializations::F' first declared as 'class' here" } +template +struct F; // { dg-warning "'class PartialSpecializations::F' declared with a mismatched class-key" } + +} diff --git a/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C new file mode 100644 index 00000000000..91d6d4abd54 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C @@ -0,0 +1,347 @@ +// Test to verify that -Wstruct-not-pod is issued for struct definitions +// that don't meet the requirements for a POD class. +// { dg-do compile } +// { dg-options "-Wstruct-not-pod -ftrack-macro-expansion=0" } + +#if __cplusplus > 199711L +#define ASSERT_POD(result, T) \ + static_assert (result == __is_pod (T), #T "is pod") +#else +#define ASSERT_POD(result, T) \ + typedef int StaticAssert [1 - 2 * (result != __is_pod (T))] +#endif + +namespace PodStruct +{ +struct A { }; ASSERT_POD (true, A); +struct B { int i; const int j; }; ASSERT_POD (true, B); +struct C { void f (); }; ASSERT_POD (true, C); +struct D { void operator= (int); }; ASSERT_POD (true, D); + +#if __cplusplus > 199711L +struct E: A { }; +struct F: E { }; +struct G1: private E { }; ASSERT_POD (true, G1); +struct G2: private F { }; ASSERT_POD (true, G2); +struct G3: A, B { }; ASSERT_POD (true, G3); +#endif + +struct H { public: int i; }; ASSERT_POD (true, H); + +#if __cplusplus > 199711L +struct I { protected: int i; protected: int j; }; ASSERT_POD (true, I); +#endif + +class J { J (); ~J (); }; +struct K { static const int i; static int &r; static J j; int k; }; +ASSERT_POD (true, K); +} + + +namespace PodTemplate +{ +template struct A { }; +template struct A; + +template struct B { int i; }; +template struct B; + +template struct C { void f (); }; +template struct C; + +template struct D { void operator= (int); }; +template struct D; + +#if __cplusplus > 199711L +template struct E: A { }; +template struct E; + +template struct F: E { }; +template struct F; + +template struct G: private A { }; +template struct G; +#endif + +template struct H { public: int i; }; +template struct H; + +#if __cplusplus > 199711L +template struct I { protected: int i; protected: int j; }; +template struct I; +#endif + +// This is considered a POD even though instantiating it on a non-POD +// will prevent it from being one. +template struct J { T i; }; +template struct J; +} + + +namespace PodExplicitSpecialization +{ +template class A; +template <> struct A { }; + +template class B; +template <> struct B { int i; }; +template class C; +template <> struct C { void f (); }; +template class D; +template <> struct D { void operator= (int); }; + +#if __cplusplus > 199711L +template class E; +template <> struct E: A { }; + +template class F; +template <> struct F: E { }; + +template class G; +template <> struct G: private A { }; +#endif + +template class H; +template <> struct H { public: int i; }; + +#if __cplusplus > 199711L +template class I; +template <> struct I { protected: int i; protected: int j; }; +#endif + +} + + +namespace PodPartialSpecialization +{ +template class A; +template struct A { }; +template struct A; + +template class B; +template struct B { int i; }; +template struct B; + +template class C; +template struct C { void f (); }; +template struct C; + +template class D; +template struct D { void operator= (int); }; +template struct D; + +#if __cplusplus > 199711L +template class E; +template struct E: A { }; +template struct E; + +template class F; +template struct F: E { }; +template struct F; + +template class G; +template struct G: private A { }; +template struct G; +#endif + +template class H; +template struct H { public: int i; }; +template struct H; + +#if __cplusplus > 199711L +template class I; +template struct I { protected: int i; protected: int j; }; +template struct I; +#endif + +// Similar to the case of the primary template, this is considered a POD +// even though instantiating it on a non-POD will prevent it from being +// one. +template class J; +template struct J { T i; }; +template struct J; +} + + +namespace NonPodStructDueToSpecialFunctions +{ +struct A // { dg-warning "non-POD type '\[A-Za-z\]\*::A' declared with class-key 'struct'; use 'class' instead" } +{ A (); }; + +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +struct D // { dg-warning "\\\[-Wstruct-not-pod" } +{ ~D (); }; + +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +} + + +namespace NonPodTemplateDueToSpecialFunctions +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; + +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +template +struct D // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } } +{ ~D (); }; + +template +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; + +template +struct F // { dg-warning "\\\[-Wstruct-not-pod" } +{ + template F (const F&); + template F& operator= (const F&); +}; +ASSERT_POD (false, F); +} + + +namespace NonPodExplicitSpecializationDueToSpecialFunctions +{ +template class A; +template <> +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; + +template class B; +template <> +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +template class C; +template <> +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +template class D; +template <> +struct D // { dg-warning "\\\[-Wstruct-not-pod" } +{ ~D (); }; + +template class E; +template <> +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +} + + +namespace NonPodPartialSpecializationDueToSpecialFunctions +{ +template class A; +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; +template struct A; + +template class B; +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; +template struct B; + +template class C; +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; +template struct C; + +template class D; +template +struct D // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } } +{ ~D (); }; +template struct D; + +template class E; +template +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +template struct E; +} + + +namespace NonPodDueToVirtuals +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ virtual void f (); }; + +} + + +namespace NonPodDueToNonPodMembers +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int &r; }; + +class B { public: B (); }; + +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ B b; }; +} + + +namespace NonPodTemplateDueToNonPodMembers +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int &r; }; + +class B { public: B (); }; + +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ B b; }; +} + + +namespace NonPodDueToAccess +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; private: int j; }; + +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; protected: int j; }; +} + + +namespace NonPodTemplateDueToAccess +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; private: int j; }; + +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; protected: int j; }; +} + + +namespace NonPodDueToBases +{ +struct A { }; +struct B { }; +struct C: A { }; +struct D: A { }; +struct E: C, D // { dg-warning "\\\[-Wstruct-not-pod" "pr83374" { xfail *-*-* } } +{ }; + +struct F: virtual A // { dg-warning "\\\[-Wstruct-not-pod" } +{ }; +} From patchwork Mon Jul 8 22:00:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 1129378 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-504657-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="GPzzQtro"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="YBae0/Wp"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45jKC360rRz9sMr for ; Tue, 9 Jul 2019 08:00:47 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:from:to:references:message-id:date:mime-version :in-reply-to:content-type; q=dns; s=default; b=kQOF98O2kzgjXPicO sMX4lh7jM63JBsb/y3y4vYEjvbw+RrHcMIVGRsmnw5fYpkYj0bibbe3IZmrr2ev/ AH45atkNUkhCMC++OTf0g9eagrSWdUPCIxdoShhadaOKgk8jZB5/oftt5mIpwnUT ARshqlNiCOX040Gp538in8pJo0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:from:to:references:message-id:date:mime-version :in-reply-to:content-type; s=default; bh=fAI9fflubg1vEIMw00ofMMf n+yw=; b=GPzzQtrocir7c+gccoTK7kQxPvWbV5c8F0EWlp33Iwq2YJR3svn2fHp KbgNEMt+TdW2pEuIc7Ty0QRHmWgtL2O8+JbWXAel6P1bY3uPJajmjRQIFvqrqJjK DXrENKFIkmP6zY/kuTYhnk1Nq2uAQdK76NGq0vzBHITB3lVCJomc= Received: (qmail 17726 invoked by alias); 8 Jul 2019 22:00:39 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 17543 invoked by uid 89); 8 Jul 2019 22:00:30 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.6 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.1 spammy= X-HELO: mail-qt1-f174.google.com Received: from mail-qt1-f174.google.com (HELO mail-qt1-f174.google.com) (209.85.160.174) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 08 Jul 2019 22:00:26 +0000 Received: by mail-qt1-f174.google.com with SMTP id k10so12002731qtq.1 for ; Mon, 08 Jul 2019 15:00:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:from:to:references:message-id:date:user-agent:mime-version :in-reply-to:content-language; bh=6TeH4CXCCvg/PFhOM8d7bL9Hk5nVP+hTtGCscIwOIHo=; b=YBae0/Wpr+uIh4/LVpU1dc9N8TSnMJws1aViWhCgTH0sVnumRaxscHWCMi49Zb1EOL D9n28FEkcSWDKmMTEzbzzPU2bbc3sXW+S9mW338x/6tafqBxzJmP2EmhWExhAkjj9Tqx BQxjVlGY+uHdEfdlQS3meHCsY3pN2WxKkKPUH9U6Vq8fgZuTXKKwQr2l8QOwT7IkqHdW uY0QA61PGb2OSYDmWYzsQSHOKh2ywMG/jF7UxEwOTf+zQwNpmbSWn9+rjeKPsXT/oGYd PrFF4molKoxSqS0eBjllxpc8o+4S0IQgw7Q9G7ItugHqseJ/m/K+JATCXt9FKZRfzbz1 BuWg== Received: from [192.168.0.41] (174-16-116-9.hlrn.qwest.net. [174.16.116.9]) by smtp.gmail.com with ESMTPSA id u19sm8469866qka.35.2019.07.08.15.00.18 for (version=TLS1_3 cipher=AEAD-AES128-GCM-SHA256 bits=128/128); Mon, 08 Jul 2019 15:00:19 -0700 (PDT) Subject: [PATCH 2/3] change class-key of PODs to struct and others to class (PR 61339) From: Martin Sebor To: gcc-patches References: Message-ID: <05c26a42-d2ed-493c-441d-5325dafa6e53@gmail.com> Date: Mon, 8 Jul 2019 16:00:18 -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: X-IsSubscribed: yes The attached patch changes the class-key of class definitions that satisfy the requirements on a POD struct to 'struct', and that of struct definitions that aren't POD to class, according to the GCC coding convention. The patch is also prerequisite for GCC being able to compile cleanly with -Wmismatched-tags. I made the changes building GCC with -Wstruct-not-pod and -Wclass-is-pod enabled, scanning the build log for instances of each warning, and using a script replacing the class-key as necessary and adjusting the access of the members declared immediately after the class-head. Martin PR c++/61339 - add mismatch between struct and class [-Wmismatched-tags] to non-bugs gcc/c-family/ChangeLog: PR c++/61339 * c.opt: gcc/cp/ChangeLog: PR c++/61339 * parser.c (cp_parser_type_specifier): (cp_parser_function_definition_after_declarator): (cp_parser_template_declaration_after_parameters): gcc/testsuite/ChangeLog: PR c++/61339 * g++.dg/warn/Wclass-is-pod.C: New test. * g++.dg/warn/Wstruct-not-pod.C: New test. diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 080066fa608..27b413115e3 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -794,6 +794,14 @@ Wstringop-truncation C ObjC C++ LTO ObjC++ Var(warn_stringop_truncation) Warning Init (1) LangEnabledBy(C ObjC C++ LTO ObjC++, Wall) Warn about truncation in string manipulation functions like strncat and strncpy. +Wstruct-not-pod +C++ ObjC++ Var(warn_struct_not_pod) Init (1) LangEnabledBy(C++ ObjC++, Wall) +Warn about structs that are not POD. + +Wclass-is-pod +C++ ObjC++ Var(warn_class_is_pod) Init (1) LangEnabledBy(C++ ObjC++, Wall) +Warn about classes that are POD. + Wsuggest-attribute=format C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning Warn about functions which might be candidates for format attributes. diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 12814102465..e20c26b7ecd 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -262,6 +262,8 @@ static bool cp_parser_omp_declare_reduction_exprs static void cp_finalize_oacc_routine (cp_parser *, tree, bool); +static void maybe_warn_struct_vs_class (location_t, tree); + /* Manifest constants. */ #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) #define CP_SAVED_TOKEN_STACK 5 @@ -17442,6 +17444,8 @@ cp_parser_type_specifier (cp_parser* parser, type_spec, token, /*type_definition_p=*/true); + + maybe_warn_struct_vs_class (token->location, type_spec); return type_spec; } @@ -28039,6 +28043,118 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, return fn; } +/* Return true if the template TYPE appears to meet the requirements + of a POD type even if some of its instantiations may not. */ + +static bool +template_pod_p (tree type) +{ + if (TYPE_HAS_USER_CONSTRUCTOR (type) + || TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) + || (TYPE_HAS_COPY_ASSIGN (type) + && (cxx_dialect != cxx98 + || !TYPE_HAS_TRIVIAL_COPY_ASSIGN (type)))) + return false; + + for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + { + if (TREE_CODE (fld) == FIELD_DECL + && !TREE_STATIC (fld) + && TREE_TYPE (fld)) + { + tree fldtype = TREE_TYPE (fld); + if (TREE_CODE (fldtype) == REFERENCE_TYPE) + return false; + if (TREE_CODE (fldtype) == RECORD_TYPE + && !template_pod_p (fldtype)) + return false; + } + else if (TREE_CODE (fld) == FUNCTION_DECL + && DECL_NONSTATIC_MEMBER_FUNCTION_P (fld) + && DECL_VIRTUAL_P (fld)) + return false; + } + + return true; +} + +/* For a DECL of class type, issue a warning when it is a POD type + and is declared with the class-key class, or when it is not a POD + type and is declared withe the class-key struct. When DECL refers + to a class template, consider instead whether it has a ctor, dtor, + or copy assignment operator as a proxy. */ + +static void +maybe_warn_struct_vs_class (location_t loc, tree type) +{ + if (TREE_CODE (type) != RECORD_TYPE) + return; + + const char *key = class_key_or_enum_as_string (type); + if (processing_template_decl) + { + if (template_pod_p (type)) + { + if (!strcmp (key, "class")) + warning_at (loc, OPT_Wclass_is_pod, + "POD-like template %qT declared with class-key %qs; " + "use %qs instead", + type, key, "struct"); + else + inform (loc, + "POD-like template %qT declared with class-key %qs " + "as expected", + type, key); + } + else if (strcmp (key, "class")) + warning_at (loc, OPT_Wstruct_not_pod, + "non-POD-like template %qT declared with class-key %qs; " + "use %qs instead", + type, key, "class"); + else + inform (loc, + "non-POD-like template %qT declared with class-key %qs " + "as expected", + type, key); + } + else + { + if (pod_type_p (type)) + { + if (!strcmp (key, "class")) + warning_at (loc, OPT_Wclass_is_pod, + "POD type %qT declared with class-key %qs; " + "use %qs instead", + type, key, "struct"); + else + inform (loc, + "POD type %qT declared with class-key %qs as expected", + type, key); + } + else if (cxx_dialect == cxx98 && template_pod_p (type)) + { + if (!strcmp (key, "class")) + warning_at (loc, OPT_Wstruct_not_pod, + "C++11 POD type %qT declared with class-key %qs; " + "use %qs instead", + type, key, "struct"); + else + inform (loc, + "C++11 POD type %qT declared with class-key %qs as expected", + type, key); + } + else if (strcmp (key, "class")) + warning_at (loc, OPT_Wstruct_not_pod, + "non-POD type %qT declared with class-key %qs; " + "use %qs instead", + type, key, "class"); + else + inform (loc, + "non-POD type %qT declared with class-key %qs as expected", + type, key); + } +} + /* Parse a template-declaration body (following argument list). */ static void @@ -28076,6 +28192,8 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser, member_p, /*explicit_specialization_p=*/false, &friend_p); + // maybe_warn_struct_vs_class (token->location, TREE_TYPE (decl)); + pop_deferring_access_checks (); /* If this is a member template declaration, let the front diff --git a/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C new file mode 100644 index 00000000000..c276b469783 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wclass-is-pod.C @@ -0,0 +1,127 @@ +// { dg-do compile } +// { dg-options "-Wclass-is-pod" } + +namespace Pod +{ +class A // { dg-warning "POD type 'Pod::A' declared with class-key 'class'; use 'struct' instead" } +{ }; +class B // { dg-warning "\\\[-Wclass-is-pod" } +{ public: int i; }; +class C // { dg-warning "\\\[-Wclass-is-pod" } +{ public: void f (); }; +class D // { dg-warning "\\\[-Wclass-is-pod" } +{ void operator= (int); }; + +#if __cplusplus > 199711L +class E // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : A { }; +class F // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : E { }; +class G // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : private A { }; +#endif +} + + +namespace PodTemplate +{ +template +class A // { dg-warning "\\\[-Wclass-is-pod" } +{ }; +template +class B // { dg-warning "\\\[-Wclass-is-pod" } +{ public: int i; }; +template +class C // { dg-warning "\\\[-Wclass-is-pod" } +{ public: void f (); }; +template +class D // { dg-warning "\\\[-Wclass-is-pod" } +{ void operator= (int); }; + +#if __cplusplus > 199711L +template +class E // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : A { }; +template +class F // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : E { }; +template +class G // { dg-warning "\\\[-Wclass-is-pod" "" { target c++11 } } + : private A { }; +#endif +} + + +namespace NonPodDueToSpecialFunctions +{ +class A +{ public: A (); }; +class B +{ public: B (int); }; + +class C +{ public: C (C&); }; + +class D +{ public: ~D (); }; + +class E +{ public: void operator= (E&); }; +} + + +namespace NonPodDueToVirtuals +{ +class A +{ public: virtual void f (); }; + +} + + +namespace NonPodDueToNonPodMembers +{ +class A +{ public: int &r; }; + +class B { public: B (); }; + +class C +{ public: B b; }; +} + + +namespace NonPodTemplateDueToNonPodMembers +{ +template +class A +{ public: T &r; }; + +class B { public: B (); }; + +template +class C +{ public: B b; }; +} + + + +namespace NonPodDueToAccess +{ +class A +{ int i; public: int j; }; + +class B +{ int i; protected: int j; }; +} + + +namespace NonPodDueToBases +{ +struct A { }; +struct B { }; +class C: A, B // { dg-bogus "\\\[-Wclass-is-pod" "pr91064" { xfail c++11 } } +{ }; + +class D: virtual A +{ }; +} diff --git a/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C new file mode 100644 index 00000000000..3e238eedef3 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstruct-not-pod.C @@ -0,0 +1,336 @@ +// Test to verify that -Wstruct-not-pod is issued for struct definitions +// that don't meet the requirements for a POD class. +// { dg-do compile } +// { dg-options "-Wstruct-not-pod" } + +#define ASSERT_POD(T) static_assert (__is_pod (T), #T "is pod") + +namespace PodStruct +{ +struct A { }; ASSERT_POD (A); +struct B { int i; const int j; }; ASSERT_POD (B); +struct C { void f (); }; ASSERT_POD (C); +struct D { void operator= (int); }; ASSERT_POD (D); + +#if __cplusplus > 199711L +struct E: A { }; +struct F: E { }; +struct G: private A { }; ASSERT_POD (G); +#endif + +struct H { public: int i; }; ASSERT_POD (H); + +#if __cplusplus > 199711L +struct I { protected: int i; protected: int j; }; ASSERT_POD (J); +#endif + +class J { J (); ~J (); }; +struct K { static const int i; static int &r; static J j; int k; }; +ASSERT_POD (K); +} + + +namespace PodTemplate +{ +template struct A { }; +template struct A { }; + +template struct B { int i; }; +template struct B { }; + +template struct C { void f (); }; +template struct C { }; + +template struct D { void operator= (int); }; +template struct D { }; + +#if __cplusplus > 199711L +template struct E: A { }; +template struct E; + +template struct F: E { }; +template struct F; + +template struct G: private A { }; +template struct G; +#endif + +template struct H { public: int i; }; +template struct H; + +#if __cplusplus > 199711L +template struct I { protected: int i; protected: int j; }; +template struct I; +#endif + +// This is considered a POD even though instantiating it on a non-POD +// will prevent it from being one. +template struct J { T i; }; +template struct J; + +template struct K { + /* Template ctor and assignment operator are not special members. */ + template K (const K&); + template K& operator= (const K&); +}; +ASSERT_POD (K); +} + + +namespace PodExplicitSpecialization +{ +template class A; +template <> struct A { }; + +template class B; +template <> struct B { int i; }; +template class C; +template <> struct C { void f (); }; +template class D; +template <> struct D { void operator= (int); }; + +#if __cplusplus > 199711L +template class E; +template <> struct E: A { }; + +template class F; +template <> struct F: E { }; + +template class G; +template <> struct G: private A { }; +#endif + +template class H; +template <> struct H { public: int i; }; + +#if __cplusplus > 199711L +template class I; +template <> struct I { protected: int i; protected: int j; }; +#endif + +} + + +namespace PodPartialSpecialization +{ +template class A; +template struct A { }; +template struct A; + +template class B; +template struct B { int i; }; +template struct B; + +template class C; +template struct C { void f (); }; +template struct C; + +template class D; +template struct D { void operator= (int); }; +template struct D; + +#if __cplusplus > 199711L +template class E; +template struct E: A { }; +template struct E; + +template class F; +template struct F: E { }; +template struct F; + +template class G; +template struct G: private A { }; +template struct G; +#endif + +template class H; +template struct H { public: int i; }; +template struct H; + +#if __cplusplus > 199711L +template class I; +template struct I { protected: int i; protected: int j; }; +template struct I; +#endif + +// Similar to the case of the primary template, this is considered a POD +// even though instantiating it on a non-POD will prevent it from being +// one. +template class J; +template struct J { T i; }; +template struct J; +} + + +namespace NonPodStructDueToSpecialFunctions +{ +struct A // { dg-warning "non-POD type '\[A-Za-z\]\*::A' declared with class-key 'struct'; use 'class' instead" } +{ A (); }; + +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +struct D // { dg-warning "\\\[-Wstruct-not-pod" } +{ ~D (); }; + +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +} + + +namespace NonPodTemplateDueToSpecialFunctions +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; + +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +template +struct D // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } } +{ ~D (); }; + +template +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +} + + +namespace NonPodExplicitSpecializationDueToSpecialFunctions +{ +template class A; +template <> +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; + +template class B; +template <> +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; + +template class C; +template <> +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; + +template class D; +template <> +struct D // { dg-warning "\\\[-Wstruct-not-pod" } +{ ~D (); }; + +template class E; +template <> +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +} + + +namespace NonPodPartialSpecializationDueToSpecialFunctions +{ +template class A; +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ A (); }; +template struct A; + +template class B; +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ B (int); }; +template struct B; + +template class C; +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ C (C&); }; +template struct C; + +template class D; +template +struct D // { dg-warning "\\\[-Wstruct-not-pod" "FIXME" { xfail *-*-* } } +{ ~D (); }; +template struct D; + +template class E; +template +struct E // { dg-warning "\\\[-Wstruct-not-pod" } +{ void operator= (E&); }; +template struct E; +} + + +namespace NonPodDueToVirtuals +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ virtual void f (); }; + +} + + +namespace NonPodDueToNonPodMembers +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int &r; }; + +class B { public: B (); }; + +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ B b; }; +} + + +namespace NonPodTemplateDueToNonPodMembers +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int &r; }; + +class B { public: B (); }; + +template +struct C // { dg-warning "\\\[-Wstruct-not-pod" } +{ B b; }; +} + + +namespace NonPodDueToAccess +{ +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; private: int j; }; + +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; protected: int j; }; +} + + +namespace NonPodTemplateDueToAccess +{ +template +struct A // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; private: int j; }; + +template +struct B // { dg-warning "\\\[-Wstruct-not-pod" } +{ int i; protected: int j; }; +} + + +namespace NonPodDueToBases +{ +struct A { }; +struct B { }; +struct C: A, B // { dg-warning "\\\[-Wstruct-not-pod" "pr91064" { xfail c++11 } } +{ }; + +struct D: virtual A // { dg-warning "\\\[-Wstruct-not-pod" } +{ }; +}