https://gcc.gnu.org/g:1ec0c5428cf84e52e5148633591f0270a566bb88

commit r17-1995-g1ec0c5428cf84e52e5148633591f0270a566bb88
Author: Jakub Jelinek <[email protected]>
Date:   Tue Jun 30 09:22:16 2026 +0200

    c++, libcpp: Implement C++29 P3540R3 - #embed offset parameter [PR125837]
    
    The following patch implements the C++29 P3540R3 - #embed offset parameter
    paper.
    
    2026-06-30  Jakub Jelinek  <[email protected]>
    
            PR c++/125837
    gcc/
            * doc/invoke.texi (Wc++29-extensions): Document.
    gcc/c-family/
            * c.opt (Wc++29-extensions): New option.
            * c-cppbuiltin.cc (c_cpp_builtins): Change __cpp_pp_embed value for
            C++29.
    gcc/testsuite/
            * g++.dg/cpp29/feat-cxx29.C (__cpp_pp_embed): Expect 202606L
            rather than 202502L.
            * c-c++-common/cpp/embed-18.c: Adjust expected diagnostics.
            * g++.dg/cpp/embed-30.C: New test.
            * g++.dg/cpp/embed-31.C: New test.
            * g++.dg/cpp/embed-32.C: New test.
    libcpp/
            * include/cpplib.h: Implement C++29 P3540R3 - #embed offset 
parameter.
            (enum cpp_warning_reason): Add CPP_W_CXX29_EXTENSIONS.
            * directives.cc (EMBED_PARAMS): Add OFFSET.
            (enum embed_param_kind): Change NUM_EMBED_STD_PARAMS.
            (_cpp_parse_embed_params): Handle offset parameter for C++.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/c-family/c-cppbuiltin.cc              |  8 ++-
 gcc/c-family/c.opt                        |  4 ++
 gcc/doc/invoke.texi                       |  7 +++
 gcc/testsuite/c-c++-common/cpp/embed-18.c |  4 +-
 gcc/testsuite/g++.dg/cpp/embed-30.C       | 87 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp/embed-31.C       | 43 +++++++++++++++
 gcc/testsuite/g++.dg/cpp/embed-32.C       |  4 ++
 gcc/testsuite/g++.dg/cpp29/feat-cxx29.C   |  4 +-
 libcpp/directives.cc                      | 36 +++++++++++--
 libcpp/include/cpplib.h                   |  1 +
 10 files changed, 189 insertions(+), 9 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 900843e8d030..a9cbd6b6f244 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1115,7 +1115,8 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_deleted_function=202403L");
          cpp_define (pfile, "__cpp_variadic_friend=202403L");
          cpp_define (pfile, "__cpp_pack_indexing=202311L");
-         cpp_define (pfile, "__cpp_pp_embed=202502L");
+         if (cxx_dialect <= cxx26)
+           cpp_define (pfile, "__cpp_pp_embed=202502L");
          cpp_define (pfile, "__cpp_constexpr_virtual_inheritance=202506L");
          cpp_define (pfile, "__cpp_expansion_statements=202506L");
          if (flag_reflection)
@@ -1124,6 +1125,11 @@ c_cpp_builtins (cpp_reader *pfile)
            cpp_warn (pfile, "__cpp_impl_reflection");
          cpp_define (pfile, "__cpp_trivial_union=202502L");
        }
+      if (cxx_dialect > cxx26)
+       {
+         /* Set feature test macros for C++29.  */
+         cpp_define (pfile, "__cpp_pp_embed=202606L");
+       }
       if (flag_concepts && cxx_dialect > cxx14)
        cpp_define (pfile, "__cpp_concepts=202002L");
       else if (cxx_dialect >= cxx20)
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 323844019b96..8bad323a4378 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -531,6 +531,10 @@ Wc++26-extensions
 C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1) 
CppReason(CPP_W_CXX26_EXTENSIONS)
 Warn about C++26 constructs in code compiled with an older standard.
 
+Wc++29-extensions
+C++ ObjC++ Var(warn_cxx29_extensions) Warning Init(1) 
CppReason(CPP_W_CXX29_EXTENSIONS)
+Warn about C++29 constructs in code compiled with an older standard.
+
 Wcalloc-transposed-args
 C ObjC C++ ObjC++ Var(warn_calloc_transposed_args) Warning LangEnabledBy(C 
ObjC C++ ObjC++,Wextra)
 Warn about suspicious calls to calloc-like functions where sizeof expression 
is the earlier size argument and not the latter.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8da5f03ccbd6..4fbd25e49dd7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10120,6 +10120,13 @@ Do not warn about C++26 constructs in code being 
compiled using
 an older C++ standard.  Even without this option, some C++26 constructs
 will only be diagnosed if @option{-Wpedantic} is used.
 
+@opindex Wc++29-extensions
+@opindex Wno-c++29-extensions
+@item -Wno-c++29-extensions @r{(C++ and Objective-C++ only)}
+Do not warn about C++29 constructs in code being compiled using
+an older C++ standard.  Even without this option, some C++29 constructs
+will only be diagnosed if @option{-Wpedantic} is used.
+
 @opindex Wcast-qual
 @opindex Wno-cast-qual
 @item -Wcast-qual
diff --git a/gcc/testsuite/c-c++-common/cpp/embed-18.c 
b/gcc/testsuite/c-c++-common/cpp/embed-18.c
index 67bbe87ec470..336c65281ed4 100644
--- a/gcc/testsuite/c-c++-common/cpp/embed-18.c
+++ b/gcc/testsuite/c-c++-common/cpp/embed-18.c
@@ -26,8 +26,8 @@
 #embed "embed-18.c" gnu::base64("SA==") /* { dg-error "'gnu::base64' parameter 
can be only used with '\\\".\\\"'" } */
 #embed <embed-18.c> gnu::base64("SA==") /* { dg-error "'gnu::base64' parameter 
can be only used with '\\\".\\\"'" } */
 #embed <.> gnu::base64("SA==") /* { dg-error "'gnu::base64' parameter can be 
only used with '\\\".\\\"'" } */
-#embed "." gnu::base64("SA==") limit(3) /* { dg-error "'gnu::base64' parameter 
conflicts with 'limit' or 'gnu::offset' parameters" } */
-#embed "." gnu::base64("SA==") gnu::offset(1) /* { dg-error "'gnu::base64' 
parameter conflicts with 'limit' or 'gnu::offset' parameters" } */
+#embed "." gnu::base64("SA==") limit(3) /* { dg-error "'gnu::base64' parameter 
conflicts with 'limit' or 'offset' or 'gnu::offset' parameters" } */
+#embed "." gnu::base64("SA==") gnu::offset(1) /* { dg-error "'gnu::base64' 
parameter conflicts with 'limit' or 'offset' or 'gnu::offset' parameters" } */
 #if 1 + __has_embed ("." gnu::base64("") __gnu__::__base64__("")) /* { 
dg-error "duplicate embed parameter 'gnu::base64'" } */
 #endif
 #if 1 + __has_embed (__FILE__ __gnu__::__base64__ prefix() suffix()) /* { 
dg-error "expected '\\\('" } */
diff --git a/gcc/testsuite/g++.dg/cpp/embed-30.C 
b/gcc/testsuite/g++.dg/cpp/embed-30.C
new file mode 100644
index 000000000000..10ceebb692f0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp/embed-30.C
@@ -0,0 +1,87 @@
+// { dg-do run }
+// { dg-options "--embed-dir=${srcdir}/c-c++-common/cpp/embed-dir 
-Wno-c++29-extensions" }
+
+#if __has_embed (__FILE__ offset (4 + FOOBAR) limit (3)) != 
__STDC_EMBED_FOUND__
+#error "__has_embed fail"
+#endif
+
+#embed <magna-carta.txt> limit(1) offset (0) prefix(int a = ) suffix (;) 
+#embed <magna-carta.txt> limit(1) offset (1 * 1) prefix(int b = ) suffix (;) 
+#embed <magna-carta.txt> limit(1) __offset__ (1 + 1) prefix(int c = ) suffix 
(;) 
+#embed <magna-carta.txt> __limit__(1) __offset__ (1 + (1 \
+  + 1)) __prefix__(int d = ) __suffix__ (;)
+const unsigned char e[] = {
+  #embed <magna-carta.txt> limit(5) offset (999)
+};
+const unsigned char f[] = {
+  #embed <magna-carta.txt> limit(7) offset (998)
+};
+const unsigned char g[] = {
+  #embed <magna-carta.txt> limit(8) offset (998)
+};
+const unsigned char h[] = {
+  #embed <magna-carta.txt> limit(8) offset (997)
+};
+const unsigned char i[] = {
+  #embed <magna-carta.txt> limit(9) offset (997)
+};
+const unsigned char j[] = {
+  #embed <magna-carta.txt> limit(30) offset (990)
+};
+const unsigned char k[] = {
+  #embed <magna-carta.txt> limit(26) offset (992)
+};
+const unsigned char l[] = {
+  #embed <magna-carta.txt>
+};
+const unsigned char m[] = {
+  #embed <magna-carta.txt> __limit__ (1000) __offset__ (32)
+};
+#if __has_embed (<magna-carta.txt> limit(5) offset (999)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(5) offset (999)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(7) offset (998)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(8) offset (998)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(8) offset (997)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(9) offset (997)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(30) offset (990)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(26) offset (992)) != 
__STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt>) != __STDC_EMBED_FOUND__ \
+    || __has_embed (<magna-carta.txt> limit(26) offset (992)) != 
__STDC_EMBED_FOUND__
+#error "__has_embed fail"
+#endif
+
+#ifdef __cplusplus
+#define C "C"
+#else
+#define C
+#endif
+extern C void abort (void);
+extern C int memcmp (const void *, const void *, __SIZE_TYPE__);
+
+int
+main ()
+{
+  if (a != 'H' || b != 'e' || c != 'n' || d != 'r')
+    abort ();
+  if (sizeof (e) != 5
+      || sizeof (f) != 7
+      || sizeof (g) != 8
+      || sizeof (h) != 8
+      || sizeof (i) != 9
+      || sizeof (j) != 30
+      || sizeof (k) != 26
+      || sizeof (l) < 1032
+      || sizeof (m) != 1000)
+    abort ();
+  if (memcmp (e, l + 999, 5)
+      || memcmp (f, l + 998, 7)
+      || memcmp (g, l + 998, 8)
+      || memcmp (h, l + 997, 8)
+      || memcmp (i, l + 997, 9)
+      || memcmp (j, l + 990, 30)
+      || memcmp (k, l + 992, 26)
+      || memcmp (m, l + 32, 1000))
+    abort ();
+  if (l[0] != 'H' || l[1] != 'e' || l[2] != 'n' || l[3] != 'r')
+    abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp/embed-31.C 
b/gcc/testsuite/g++.dg/cpp/embed-31.C
new file mode 100644
index 000000000000..0d9dcf140708
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp/embed-31.C
@@ -0,0 +1,43 @@
+// { dg-do preprocess }
+// { dg-options "-Wno-c++29-extensions" }
+
+#embed __FILE__ offset(1) offset(1) // { dg-error "duplicate embed parameter 
'offset'" }
+#embed __FILE__ offset prefix() suffix() // { dg-error "expected '\\\('" }
+#embed __FILE__ offset (1 / 0) // { dg-error "division by zero in #embed" }
+#embed __FILE__ __offset__ (+ + +) // { dg-error "operator '\\\+' has no right 
operand" }
+#define FOO 1
+#embed __FILE__ offset(0 + defined(FOO)) // { dg-error "'defined' in '#embed' 
parameter" }
+#embed __FILE__ offset (-1) // { dg-error "negative embed parameter operand" }
+#embed __FILE__ offset (-42) // { dg-error "negative embed parameter operand" }
+#embed __FILE__ offset (-9223372036854775807 - 1) // { dg-error "negative 
embed parameter operand" }
+#embed __FILE__ offset (18446744073709551615ULL) // { dg-error "too large 
'offset' argument" }
+#embed __FILE__ offset(0) gnu::offset(0) // { dg-error "'gnu::offset' 
parameter conflicts with 'offset' parameter" }
+#embed __FILE__ gnu::offset(0) offset(0) // { dg-error "'offset' parameter 
conflicts with 'gnu::offset' parameter" }
+#embed __FILE__ __offset__(0) __gnu__::__offset__(0) // { dg-error 
"'gnu::offset' parameter conflicts with 'offset' parameter" }
+#embed __FILE__ __gnu__::__offset__(0) __offset__(0) // { dg-error "'offset' 
parameter conflicts with 'gnu::offset' parameter" }
+#if 1 + __has_embed (__FILE__ offset(1) __offset__(1)) // { dg-error 
"duplicate embed parameter 'offset'" }
+#endif
+#if 1 + __has_embed (__FILE__ __offset__ prefix() suffix()) // { dg-error 
"expected '\\\('" }
+#endif
+#if 1 + __has_embed (__FILE__ offset(1/0)) // { dg-error "division by zero in 
#embed" }
+#endif
+#if 1 + __has_embed (__FILE__ offset(+ + +)) // { dg-error "operator '\\\+' 
has no right operand" }
+#endif
+#if 1 + __has_embed (__FILE__ offset(0 + defined(FOO))) // { dg-error 
"'defined' in '#embed' parameter" }
+#endif
+#if 1 + __has_embed (__FILE__ offset (-1)) // { dg-error "negative embed 
parameter operand" }
+#endif
+#if 1 + __has_embed (__FILE__ offset (-42)) // { dg-error "negative embed 
parameter operand" }
+#endif
+#if 1 + __has_embed (__FILE__ offset (-9223372036854775807 - 1)) // { dg-error 
"negative embed parameter operand" }
+#endif
+#if 1 + __has_embed (__FILE__ offset (18446744073709551615ULL)) // { dg-error 
"too large 'offset' argument" }
+#endif
+#if 1 + __has_embed (__FILE__ offset (0) gnu::offset (0)) // { dg-error 
"'gnu::offset' parameter conflicts with 'offset' parameter" }
+#endif
+#if 1 + __has_embed (__FILE__ gnu::offset (0) offset (0)) // { dg-error 
"'offset' parameter conflicts with 'gnu::offset' parameter" }
+#endif
+#if 1 + __has_embed (__FILE__ __offset__ (0) __gnu__::__offset__ (0)) // { 
dg-error "'gnu::offset' parameter conflicts with 'offset' parameter" }
+#endif
+#if 1 + __has_embed (__FILE__ __gnu__::__offset__ (0) __offset__ (0)) // { 
dg-error "'offset' parameter conflicts with 'gnu::offset' parameter" }
+#endif
diff --git a/gcc/testsuite/g++.dg/cpp/embed-32.C 
b/gcc/testsuite/g++.dg/cpp/embed-32.C
new file mode 100644
index 000000000000..241b6cc7824e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp/embed-32.C
@@ -0,0 +1,4 @@
+// { dg-do preprocess }
+// { dg-options "--embed-dir=${srcdir}/c-c++-common/cpp/embed-dir" }
+
+#embed <magna-carta.txt> limit(1) offset (0) prefix(int a = ) suffix (;) // { 
dg-warning "'offset' parameter before C\\\+\\\+29 is a GCC extension" "" { 
target c++26_down } }
diff --git a/gcc/testsuite/g++.dg/cpp29/feat-cxx29.C 
b/gcc/testsuite/g++.dg/cpp29/feat-cxx29.C
index bfc5366999c2..1beb50dc4ad4 100644
--- a/gcc/testsuite/g++.dg/cpp29/feat-cxx29.C
+++ b/gcc/testsuite/g++.dg/cpp29/feat-cxx29.C
@@ -631,8 +631,8 @@
 
 #ifndef __cpp_pp_embed
 #  error "__cpp_pp_embed"
-#elif __cpp_pp_embed != 202502
-#  error "__cpp_pp_embed != 202502"
+#elif __cpp_pp_embed != 202606
+#  error "__cpp_pp_embed != 202606"
 #endif
 
 #ifndef __cpp_constexpr_virtual_inheritance
diff --git a/libcpp/directives.cc b/libcpp/directives.cc
index 9accc3798e09..7aed4db44460 100644
--- a/libcpp/directives.cc
+++ b/libcpp/directives.cc
@@ -1106,6 +1106,7 @@ skip_balanced_token_seq (cpp_reader *pfile, cpp_ttype end,
   EMBED_PARAM (PREFIX, "prefix")       \
   EMBED_PARAM (SUFFIX, "suffix")       \
   EMBED_PARAM (IF_EMPTY, "if_empty")   \
+  EMBED_PARAM (OFFSET, "offset")       \
   EMBED_PARAM (GNU_BASE64, "base64")   \
   EMBED_PARAM (GNU_OFFSET, "offset")
 
@@ -1114,7 +1115,7 @@ enum embed_param_kind {
   EMBED_PARAMS
 #undef EMBED_PARAM
   NUM_EMBED_PARAMS,
-  NUM_EMBED_STD_PARAMS = EMBED_PARAM_IF_EMPTY + 1
+  NUM_EMBED_STD_PARAMS = EMBED_PARAM_OFFSET + 1
 };
 
 static struct { int len; const char *name; } embed_params[NUM_EMBED_PARAMS] = {
@@ -1158,6 +1159,7 @@ _cpp_parse_embed_params (cpp_reader *pfile, struct 
cpp_embed_params *params)
            }
          if (params->base64.count
              && (seen & ((1 << EMBED_PARAM_LIMIT)
+                         | (1 << EMBED_PARAM_OFFSET)
                          | (1 << EMBED_PARAM_GNU_OFFSET))) != 0)
            {
              ret = false;
@@ -1165,8 +1167,8 @@ _cpp_parse_embed_params (cpp_reader *pfile, struct 
cpp_embed_params *params)
                cpp_error_with_line (pfile, CPP_DL_ERROR,
                                     params->base64.base_run.base->src_loc, 0,
                                     "%<gnu::base64%> parameter conflicts "
-                                    "with %<limit%> or %<gnu::offset%> "
-                                    "parameters");
+                                    "with %<limit%> or %<offset%> or "
+                                    "%<gnu::offset%> parameters");
            }
          else if (params->base64.count == 0
                   && CPP_OPTION (pfile, preprocessed))
@@ -1241,6 +1243,13 @@ _cpp_parse_embed_params (cpp_reader *pfile, struct 
cpp_embed_params *params)
                && memcmp (param_name, embed_params[i].name,
                           param_name_len) == 0)
              {
+               /* Until C standardizes offset, people should use just
+                  gnu::offset in C.  When/if it is standardized, these
+                  3 lines should be removed and pedwarn condition and
+                  wording adjusted to cope with C too.  */
+               if (i == EMBED_PARAM_OFFSET
+                   && !CPP_OPTION (pfile, cplusplus))
+                 continue;
                param_kind = i;
                break;
              }
@@ -1285,6 +1294,7 @@ _cpp_parse_embed_params (cpp_reader *pfile, struct 
cpp_embed_params *params)
        cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0,
                             "expected %<(%>");
       else if (param_kind == EMBED_PARAM_LIMIT
+              || param_kind == EMBED_PARAM_OFFSET
               || param_kind == EMBED_PARAM_GNU_OFFSET)
        {
          if (params->has_embed && pfile->op_stack == NULL)
@@ -1294,9 +1304,27 @@ _cpp_parse_embed_params (cpp_reader *pfile, struct 
cpp_embed_params *params)
            params->limit = res;
          else
            {
+             if ((seen & ((1 << EMBED_PARAM_OFFSET)
+                          | (1 << EMBED_PARAM_GNU_OFFSET)))
+                 == ((1 << EMBED_PARAM_OFFSET)
+                     | (1 << EMBED_PARAM_GNU_OFFSET)))
+               cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0,
+                                    "%qs parameter conflicts with %qs "
+                                    "parameter",
+                                    param_kind == EMBED_PARAM_OFFSET
+                                    ? "offset" : "gnu::offset",
+                                    param_kind == EMBED_PARAM_OFFSET
+                                    ? "gnu::offset" : "offset");
+             if (param_kind == EMBED_PARAM_OFFSET
+                 && CPP_OPTION (pfile, lang) < CLK_GNUCXX29)
+               cpp_pedwarning_with_line (pfile, CPP_W_CXX29_EXTENSIONS, loc,
+                                         0, "%qs parameter before C++29 is "
+                                            "a GCC extension", "offset");
              if (res > INTTYPE_MAXIMUM (off_t))
                cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0,
-                                    "too large %<gnu::offset%> argument");
+                                    "too large %qs argument",
+                                    param_kind == EMBED_PARAM_OFFSET
+                                    ? "offset" : "gnu::offset");
              else
                params->offset = res;
            }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 98add66b13ab..8bb6deac351f 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -761,6 +761,7 @@ enum cpp_warning_reason {
   CPP_W_CXX20_EXTENSIONS,
   CPP_W_CXX23_EXTENSIONS,
   CPP_W_CXX26_EXTENSIONS,
+  CPP_W_CXX29_EXTENSIONS,
   CPP_W_EXPANSION_TO_DEFINED,
   CPP_W_BIDIRECTIONAL,
   CPP_W_INVALID_UTF8,

Reply via email to