From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 93EFC3858D28 for ; Fri, 5 Nov 2021 20:01:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 93EFC3858D28 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-394-XskdlRVYP36PySZGOnwoaQ-1; Fri, 05 Nov 2021 16:01:38 -0400 X-MC-Unique: XskdlRVYP36PySZGOnwoaQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6F5911966320; Fri, 5 Nov 2021 20:01:37 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.39.192.25]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 41ED613ABD; Fri, 5 Nov 2021 20:01:36 +0000 (UTC) From: Florian Weimer To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org Subject: [RFC PATCH] Implement #pragma GCC noexpand Date: Fri, 05 Nov 2021 21:01:34 +0100 Message-ID: <87pmres76p.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 05 Nov 2021 20:01:42 -0000 This can be used to avoid excessive __ mangling of identifiers merely to guard against accidental macro expansion. (Identifiers in the global scope or with external linkage may still need mangling.) In order to support -fdirectives-only without introducing new line change flags, #include is not permitted in noexpand mode. I think this could be particularly useful for writing libstdc++ headers. Is this something we want? Then I'll figure out how to add some tests. Thanks, Florian libcpp/ChangeLog * include/cpplib. (enum cpp_warning_reason): Add CPP_W_EXPANSION_TO_DEFINED. (NODE_EXPAND): Define new node flag. (struct cpp_hashnode): Expand width of flags member. * internal.h (struct cpp_read): Add noexpand member. * directives.c (struct pragma_entry): Add pass_if_directives_only. (do_include_common): Error out in noexpand mode. (do_linemarker): Reset noexpand flag when leaving files. (_cpp_pop_buffer): Likewise. (register_pragma_internal_not_directive_only) (do_pragma_expand, do_pragma_noexpand): New functions. (_cpp_init_internal_pragmas): Register "GCC expand", "GCC noexpand" pragmata. (do_pragma): Use default callback for pass_if_directives_only pragmata with -fdirectives-only. * init.c (cpp_init_builtins): Define _Expand helper macro. * macro.c (enter_macro_context): Enable nested macro expansion by disabling noexpand. (cpp_get_token_1): Skip macro expansion in noexpand mode, except for NODE_EXPAND macros. gcc/ChangeLog * c-family/c.opt (Wcpp-noexpand): Add. * common.opt (Wcpp-noexpand): Likewise. * doc/cpp.texi (Common Predefined Macros): Document _Expand. (Pragmas): Document "GCC noexpand", "GCC expand". * doc/invoke.texi (Option Summary): Add -Wno-cpp-noexpand. (Warning Options): Document -Wno-cpp-noexpand. --- gcc/c-family/c.opt | 4 +++ gcc/common.opt | 4 +++ gcc/doc/cpp.texi | 26 +++++++++++++++++++ gcc/doc/invoke.texi | 7 +++++- libcpp/directives.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ libcpp/include/cpplib.h | 6 +++-- libcpp/init.c | 6 +++++ libcpp/internal.h | 3 +++ libcpp/macro.c | 14 ++++++++--- 9 files changed, 130 insertions(+), 6 deletions(-) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 06457ac739e..4cb9811db3a 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -511,6 +511,10 @@ Wcpp C ObjC C++ ObjC++ CppReason(CPP_W_WARNING_DIRECTIVE) ; Documented in common.opt +Wcpp-noexpand +C ObjC C++ ObjC++ CppReason(CPP_W_NOEXPAND) +; Documented in common.opt. + Wctad-maybe-unsupported C++ ObjC++ Var(warn_ctad_maybe_unsupported) Warning Warn when performing class template argument deduction on a type with no diff --git a/gcc/common.opt b/gcc/common.opt index 1a5b9bfcca9..d5f6a5c296d 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -574,6 +574,10 @@ Wcpp Common Var(warn_cpp) Init(1) Warning Warn when a #warning directive is encountered. +Wcpp-noexpand +Common Var(warn_cpp_noexpand) Init(1) Warning +Warn about misuses of the noexpand preprocessor feature. + Wattribute-warning Common Var(warn_attribute_warning) Init(1) Warning Warn about uses of __attribute__((warning)) declarations. diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi index 53f7204504c..037dc51cba3 100644 --- a/gcc/doc/cpp.texi +++ b/gcc/doc/cpp.texi @@ -1941,6 +1941,11 @@ generate unique identifiers. Care must be taken to ensure that @code{__COUNTER__} is not expanded prior to inclusion of precompiled headers which use it. Otherwise, the precompiled headers will not be used. +@item _Expand +@samp{_Expand (@var{source})} can be used to macro-expand @var{source} +after a @samp{#pragma GCC noexpand} directive, where macro expansion is +inhibited otherwise. + @item __GFORTRAN__ The GNU Fortran compiler defines this. @@ -3843,6 +3848,27 @@ file will never be read again, no matter what. It is a less-portable alternative to using @samp{#ifndef} to guard the contents of header files against multiple inclusions. +@item #pragma GCC noexpand +Temporarily turns off macro expansion. In order to expand @var{source} +in this mode, write @samp{_Expand (@var{source})}. For example, + +@smallexample +#define A a +#define B b +#define C() A B +#pragma GCC noexpand +A _Expand (A B C()) B +@end smallexample + +expands to @samp{A a b a b B}. + +It is an error to include other files in @code{noexpand}, until macro +expansion has been turned on again using @samp{#pragma GCC expand}. + +@item #pragma GCC expand +Turns on macro expansion again after it has been turned off using +@samp{#pragma GCC noexpand}. + @end ftable @node Other Directives diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 9fb74d34920..4e6c5210161 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -338,7 +338,7 @@ Objective-C and Objective-C++ Dialects}. -Wcast-align -Wcast-align=strict -Wcast-function-type -Wcast-qual @gol -Wchar-subscripts @gol -Wclobbered -Wcomment @gol --Wconversion -Wno-coverage-mismatch -Wno-cpp @gol +-Wconversion -Wno-coverage-mismatch -Wno-cpp -Wno-cpp-noexpand @gol -Wdangling-else -Wdate-time @gol -Wno-deprecated -Wno-deprecated-declarations -Wno-designated-init @gol -Wdisabled-optimization @gol @@ -5903,6 +5903,11 @@ disable the error. @opindex Wcpp Suppress warning messages emitted by @code{#warning} directives. +@item -Wno-cpp-noexpand @r{(C, Objective-C, C++, Objective-C++ and Fortran only)} +@opindex Wno-cpp +@opindex Wcpp +Suppress warning messages due to misuse of @code{#pragma GCC noexpand}. + @item -Wdouble-promotion @r{(C, C++, Objective-C and Objective-C++ only)} @opindex Wdouble-promotion @opindex Wno-double-promotion diff --git a/libcpp/directives.c b/libcpp/directives.c index 34f7677f718..8335e1ea1be 100644 --- a/libcpp/directives.c +++ b/libcpp/directives.c @@ -46,6 +46,7 @@ struct pragma_entry bool is_nspace; bool is_internal; bool is_deferred; + bool pass_if_directives_only; bool allow_expansion; union { pragma_cb handler; @@ -126,6 +127,8 @@ static void do_pragma_dependency (cpp_reader *); static void do_pragma_warning_or_error (cpp_reader *, bool error); static void do_pragma_warning (cpp_reader *); static void do_pragma_error (cpp_reader *); +static void do_pragma_expand (cpp_reader *); +static void do_pragma_noexpand (cpp_reader *); static void do_linemarker (cpp_reader *); static const cpp_token *get_token_no_padding (cpp_reader *); static const cpp_token *get__Pragma_string (cpp_reader *); @@ -828,6 +831,14 @@ do_include_common (cpp_reader *pfile, enum include_type type) const cpp_token **buf = NULL; location_t location; + if (pfile->noexpand) + { + cpp_error (pfile, CPP_DL_ERROR, + "#%s not allowed after #pragma GCC noexpand", + pfile->directive->name); + return; + } + /* Re-enable saving of comments if requested, so that the include callback can dump comments which follow #include. */ pfile->state.save_comments = ! CPP_OPTION (pfile, discard_comments); @@ -1104,6 +1115,10 @@ do_linemarker (cpp_reader *pfile) if (reason == LC_LEAVE) { + /* Include directives are not allowed in noexpand mode. + Clear the flag unconditionally. */ + pfile->noexpand = false; + /* Reread map since cpp_get_token can invalidate it with a reallocation. */ map = LINEMAPS_LAST_ORDINARY_MAP (line_table); @@ -1341,6 +1356,23 @@ register_pragma_internal (cpp_reader *pfile, const char *space, entry->u.handler = handler; } +/* Register a cpplib internal pragma SPACE NAME with HANDLER. Unlike + register_pragma_internal, such pragmata are passed through with + -fdirectives-internal. */ +static void +register_pragma_internal_not_directive_only (cpp_reader *pfile, + const char *space, + const char *name, + pragma_cb handler) +{ + struct pragma_entry *entry; + + entry = register_pragma_1 (pfile, space, name, false); + entry->is_internal = true; + entry->pass_if_directives_only = true; + entry->u.handler = handler; +} + /* Register a pragma NAME in namespace SPACE. If SPACE is null, it goes in the global namespace. HANDLER is the handler it will call, which must be non-NULL. If ALLOW_EXPANSION is set, allow macro @@ -1401,6 +1433,10 @@ _cpp_init_internal_pragmas (cpp_reader *pfile) register_pragma_internal (pfile, "GCC", "dependency", do_pragma_dependency); register_pragma_internal (pfile, "GCC", "warning", do_pragma_warning); register_pragma_internal (pfile, "GCC", "error", do_pragma_error); + register_pragma_internal_not_directive_only (pfile, "GCC", "expand", + do_pragma_expand); + register_pragma_internal_not_directive_only (pfile, "GCC", "noexpand", + do_pragma_noexpand); } /* Return the number of registered pragmas in PE. */ @@ -1515,6 +1551,9 @@ do_pragma (cpp_reader *pfile) } } + if (p && p->pass_if_directives_only && CPP_OPTION (pfile, directives_only)) + p = NULL; + if (p) { if (p->is_deferred) @@ -1810,6 +1849,29 @@ do_pragma_error (cpp_reader *pfile) do_pragma_warning_or_error (pfile, true); } +/* Enable macro expansion. */ +static void +do_pragma_expand (cpp_reader *pfile) +{ + check_eol (pfile, false); + if (pfile->noexpand) + pfile->noexpand = false; + else + cpp_warning (pfile, CPP_W_NOEXPAND, + "#pragma GCC expand without previous #pragma GCC noexpand"); +} + +/* Disable macro expansion. */ +static void +do_pragma_noexpand (cpp_reader *pfile) +{ + check_eol (pfile, false); + if (!pfile->noexpand) + pfile->noexpand = true; + else + cpp_warning (pfile, CPP_W_NOEXPAND, "redundant #pragma GCC noexpand"); +} + /* Get a token but skip padding. */ static const cpp_token * get_token_no_padding (cpp_reader *pfile) @@ -2769,6 +2831,10 @@ _cpp_pop_buffer (cpp_reader *pfile) if (inc) { + /* Include directives are not allowed in noexpand mode. Clear the flag + unconditionally. */ + pfile->noexpand = false; + _cpp_pop_file_buffer (pfile, inc, to_free); _cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0); diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 176f8c5bbce..3118283fae0 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -643,7 +643,8 @@ enum cpp_warning_reason { CPP_W_C90_C99_COMPAT, CPP_W_C11_C2X_COMPAT, CPP_W_CXX11_COMPAT, - CPP_W_EXPANSION_TO_DEFINED + CPP_W_EXPANSION_TO_DEFINED, + CPP_W_NOEXPAND }; /* Callback for header lookup for HEADER, which is the name of a @@ -873,6 +874,7 @@ struct GTY(()) cpp_macro { #define NODE_CONDITIONAL (1 << 6) /* Conditional macro */ #define NODE_WARN_OPERATOR (1 << 7) /* Warn about C++ named operator. */ #define NODE_MODULE (1 << 8) /* C++-20 module-related name. */ +#define NODE_EXPAND (1 << 9) /* Expand even in in noexpand mode. */ /* Different flavors of hash node. */ enum node_type @@ -933,7 +935,7 @@ struct GTY(()) cpp_hashnode { then index into directive table. Otherwise, a NODE_OPERATOR. */ unsigned int rid_code : 8; /* Rid code - for front ends. */ - unsigned int flags : 9; /* CPP flags. */ + unsigned int flags : 10; /* CPP flags. */ ENUM_BITFIELD(node_type) type : 2; /* CPP node type. */ /* 5 bits spare. */ diff --git a/libcpp/init.c b/libcpp/init.c index 5a424e23553..b4b368c46cd 100644 --- a/libcpp/init.c +++ b/libcpp/init.c @@ -601,6 +601,12 @@ cpp_init_builtins (cpp_reader *pfile, int hosted) if (CPP_OPTION (pfile, objc)) _cpp_define_builtin (pfile, "__OBJC__ 1"); + + if (!CPP_OPTION (pfile, traditional)) + { + cpp_lookup (pfile, DSC("_Expand"))->flags |= NODE_EXPAND; + _cpp_define_builtin (pfile, "_Expand(x) x"); + } } /* Sanity-checks are dependent on command-line options, so it is diff --git a/libcpp/internal.h b/libcpp/internal.h index 8577cab6c83..ba44da21f7f 100644 --- a/libcpp/internal.h +++ b/libcpp/internal.h @@ -455,6 +455,9 @@ struct cpp_reader one. */ bool about_to_expand_macro_p; + /* If true, #pragma GCC noexpand is active. */ + bool noexpand; + /* Search paths for include files. */ struct cpp_dir *quote_include; /* "" */ struct cpp_dir *bracket_include; /* <> */ diff --git a/libcpp/macro.c b/libcpp/macro.c index b2f797cae35..e19a629102c 100644 --- a/libcpp/macro.c +++ b/libcpp/macro.c @@ -1493,9 +1493,14 @@ enter_macro_context (cpp_reader *pfile, cpp_hashnode *node, } if (macro->paramc > 0) - replace_args (pfile, node, macro, - (macro_arg *) buff->base, - location); + { + bool saved_noexpand = pfile->noexpand; + pfile->noexpand = false; + replace_args (pfile, node, macro, + (macro_arg *) buff->base, + location); + pfile->noexpand = saved_noexpand; + } /* Free the memory used by the arguments of this function-like macro. This memory has been allocated by funlike_invocation_p and by replace_args. */ @@ -2930,6 +2935,9 @@ cpp_get_token_1 (cpp_reader *pfile, location_t *location) node = result->val.node.node; + if (pfile->noexpand && !(node->flags & NODE_EXPAND)) + break; + if (node->type == NT_VOID || (result->flags & NO_EXPAND)) break;