[PATCH] PR125533 fortran/libcpp: CPP stringify operator emits \" for double-quotes in Fortran mode
Jerry D <[email protected]>
| Newsgroups | gmane.comp.gcc.patches,gmane.comp.gcc.fortran |
|---|---|
| Message-ID | <[email protected]> |
See attached patch. This one was a surprise and a subtle problem.
See the explanation in the commit message. Since this touches on libcpp I am not
sure who else should review this.
Regression tested on x86_64.
OK for mainline and backport to 16 later.
Regards,
Jerry
---
The traditional-mode CPP stringification operator escaped double-quote
characters in macro argument tokens with a backslash (e.g.
CPP_STRINGIFY(key .eq. "x") expanded to "key .eq. \"x\""). This is
correct for C but not valid Fortran: Fortran represents an embedded
double-quote with a doubled quote (""), not a backslash-quote (\").
The backslash-escaped output was rejected by the Fortran scanner with a
spurious syntax error.
Fix: add a new flag fortran_string_escaping to struct cpp_options. When
set, traditional.cc emits a doubled quote instead of a backslash-quote when
escaping a double-quote character inside a stringified argument, and
suppresses the extra backslash before a literal backslash in that context.
Set the flag in gfc_cpp_post_options, which configures the CPP reader for
Fortran.
PR fortran/125533
Assisted by: Claude Sonnet 4.6
gcc/fortran/ChangeLog:
* cpp.cc (gfc_cpp_post_options): Set fortran_string_escaping in
cpp_options so traditional-mode stringification uses doubled-quote
escaping instead of backslash-quote.
gcc/testsuite/ChangeLog:
* gfortran.dg/cpp_stringify_quote_1.F90: New test.
libcpp/ChangeLog:
* include/cpplib.h (cpp_options): Add fortran_string_escaping flag.
* traditional.cc (replace_args_and_push): When
fortran_string_escaping is set, escape double-quote with a doubled
quote rather than a backslash; suppress the extra backslash before a
backslash inside a quoted argument.
---
PR125533-cpp-stringify-fortran-quote.patch
(text/x-patch, 5.6 KB)
From 2b355d4f4239bfd9a2c184f5270e0d852e90f7fd Mon Sep 17 00:00:00 2001 From: Jerry DeLisle <[email protected]> Date: Fri, 5 Jun 2026 12:20:18 -0700 Subject: [PATCH] fortran/libcpp: CPP stringify operator emits \" for double-quotes in Fortran mode The traditional-mode CPP stringification operator escaped double-quote characters in macro argument tokens with a backslash (e.g. CPP_STRINGIFY(key .eq. "x") expanded to "key .eq. \"x\""). This is correct for C but not valid Fortran: Fortran represents an embedded double-quote with a doubled quote (""), not a backslash-quote (\"). The backslash-escaped output was rejected by the Fortran scanner with a spurious syntax error. Fix: add a new flag fortran_string_escaping to struct cpp_options. When set, traditional.cc emits a doubled quote instead of a backslash-quote when escaping a double-quote character inside a stringified argument, and suppresses the extra backslash before a literal backslash in that context. Set the flag in gfc_cpp_post_options, which configures the CPP reader for Fortran. PR fortran/125533 Assisted by: Claude Sonnet 4.6 gcc/fortran/ChangeLog: * cpp.cc (gfc_cpp_post_options): Set fortran_string_escaping in cpp_options so traditional-mode stringification uses doubled-quote escaping instead of backslash-quote. gcc/testsuite/ChangeLog: * gfortran.dg/cpp_stringify_quote_1.F90: New test. libcpp/ChangeLog: * include/cpplib.h (cpp_options): Add fortran_string_escaping flag. * traditional.cc (replace_args_and_push): When fortran_string_escaping is set, escape double-quote with a doubled quote rather than a backslash; suppress the extra backslash before a backslash inside a quoted argument. --- gcc/fortran/cpp.cc | 1 + .../gfortran.dg/cpp_stringify_quote_1.F90 | 29 +++++++++++++++++++ libcpp/include/cpplib.h | 6 ++++ libcpp/traditional.cc | 20 +++++++++---- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/cpp_stringify_quote_1.F90 diff --git a/gcc/fortran/cpp.cc b/gcc/fortran/cpp.cc index 6b5f136e4f3..bac34928ce4 100644 --- a/gcc/fortran/cpp.cc +++ b/gcc/fortran/cpp.cc @@ -517,6 +517,7 @@ gfc_cpp_post_options (bool verbose_missing_dir_warn) /* TODO: allow non-traditional modes, e.g. by -cpp-std=...? */ cpp_option->traditional = 1; + cpp_option->fortran_string_escaping = 1; cpp_option->cplusplus_comments = 0; cpp_option->cpp_pedantic = pedantic; diff --git a/gcc/testsuite/gfortran.dg/cpp_stringify_quote_1.F90 b/gcc/testsuite/gfortran.dg/cpp_stringify_quote_1.F90 new file mode 100644 index 00000000000..16b082ed9c0 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/cpp_stringify_quote_1.F90 @@ -0,0 +1,29 @@ +! { dg-do compile } +! { dg-options "-cpp -DASSERTIONS" } +! +! Test that \" inside a CPP-macro-expanded double-quoted character literal +! is treated as an embedded double-quote rather than a string terminator. +! The gfortran-compatible CPP_STRINGIFY_SOURCE workaround ("x" form) +! embeds the macro argument directly inside a string literal; when the +! argument contains " the CPP emits \" which must be accepted. + +! This matches the pattern in assert_macros.h (assert 3.0.2 / julienne 3.6.1) +#define CPP_STRINGIFY_SOURCE(x) "x" + +#if ASSERTIONS +# define SHOW(a) print *, "assert(" // CPP_STRINGIFY_SOURCE(a) // ")" +#else +# define SHOW(a) +#endif + +program cpp_stringify_quote + implicit none + character(len=20) :: key + + ! No double quotes in argument -- baseline + SHOW(1 + 2 .eq. 3) + + ! Double-quoted string literal in argument -- was failing before fix + SHOW(key .eq. "metadata") + SHOW(key .eq. "a" // "b") +end program diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index e8c35cd68d7..aae71eaa167 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -511,6 +511,12 @@ struct cpp_options /* True for traditional preprocessing. */ unsigned char traditional; + /* True when preprocessing Fortran source. In traditional mode, + double-quote characters in macro arguments expanded inside a + quoted context are escaped using the Fortran doubled-quote + convention ("") rather than the C backslash convention (\"). */ + unsigned char fortran_string_escaping; + /* Nonzero for C++ 2011 Standard user-defined literals. */ unsigned char user_literals; diff --git a/libcpp/traditional.cc b/libcpp/traditional.cc index 2ed07bc7346..20e67d7114e 100644 --- a/libcpp/traditional.cc +++ b/libcpp/traditional.cc @@ -1041,18 +1041,26 @@ replace_args_and_push (cpp_reader *pfile, struct fun_macro *fmacro) { if (in > base && *(in-1) != '\\') argquote = ! argquote; - /* Always add backslash before double quote if argument - is expanded in a quoted context */ - *p++ = '\\'; + /* Escape the double-quote character inside the string. + For Fortran, use the doubled-quote convention ("") so + that the result is valid Fortran; for C, use the + backslash convention (\"). */ + if (CPP_OPTION (pfile, fortran_string_escaping)) + *p++ = '"'; + else + *p++ = '\\'; len++; } else if (cxtquote && argquote && *in == '\\') { /* Always add backslash before a backslash in an argument that is expanded in a quoted context and also in the - range of a quoted context in the argument itself. */ - *p++ = '\\'; - len++; + range of a quoted context in the argument itself. */ + if (!CPP_OPTION (pfile, fortran_string_escaping)) + { + *p++ = '\\'; + len++; + } } *p++ = *in++; len++; -- 2.54.0