From: Florian Weimer <fweimer@redhat.com>
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 [thread overview]
Message-ID: <87pmres76p.fsf@oldenburg.str.redhat.com> (raw)
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;
reply other threads:[~2021-11-05 20:01 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87pmres76p.fsf@oldenburg.str.redhat.com \
--to=fweimer@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).