[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.