Hi!

The following patch on top of the just posted -std=c++29 patch
implements the C++29 P3540R3 - #embed offset parameter paper.

Ok for trunk?

2026-06-12  Jakub Jelinek  <[email protected]>

gcc/
        * doc/invoke.texi (Wc++29-extensions): Document.
gcc/c-family/
        * c.opt (Wc++29-extensions): New option.
gcc/testsuite/
        * 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++.

--- gcc/c-family/c.opt.jj       2026-06-12 17:05:03.744184261 +0200
+++ gcc/c-family/c.opt  2026-06-12 18:16:31.257686461 +0200
@@ -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.
--- gcc/doc/invoke.texi.jj      2026-06-12 17:27:00.453670490 +0200
+++ gcc/doc/invoke.texi 2026-06-12 18:17:26.167960719 +0200
@@ -10120,6 +10120,13 @@ Do not warn about C++26 constructs in co
 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
--- gcc/testsuite/c-c++-common/cpp/embed-18.c.jj        2026-03-27 
10:17:15.171313817 +0100
+++ gcc/testsuite/c-c++-common/cpp/embed-18.c   2026-06-12 18:35:31.887612939 
+0200
@@ -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 '\\\('" } */
--- gcc/testsuite/g++.dg/cpp/embed-30.C.jj      2026-06-12 18:21:21.374852038 
+0200
+++ gcc/testsuite/g++.dg/cpp/embed-30.C 2026-06-12 18:38:02.605621723 +0200
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/cpp/embed-31.C.jj      2026-03-27 10:17:15.171313817 
+0100
+++ gcc/testsuite/g++.dg/cpp/embed-31.C 2026-06-12 18:37:49.647792917 +0200
@@ -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
--- gcc/testsuite/g++.dg/cpp/embed-32.C.jj      2026-06-12 18:38:10.148522070 
+0200
+++ gcc/testsuite/g++.dg/cpp/embed-32.C 2026-06-12 18:40:37.643573435 +0200
@@ -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 } }
--- libcpp/include/cpplib.h.jj  2026-06-12 17:22:11.101508989 +0200
+++ libcpp/include/cpplib.h     2026-06-12 18:15:56.042151897 +0200
@@ -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,
--- libcpp/directives.cc.jj     2026-03-27 10:17:21.565209479 +0100
+++ libcpp/directives.cc        2026-06-12 18:15:07.736790337 +0200
@@ -1106,6 +1106,7 @@ skip_balanced_token_seq (cpp_reader *pfi
   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 *pfi
            }
          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 *pfi
                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,9 @@ _cpp_parse_embed_params (cpp_reader *pfi
                && memcmp (param_name, embed_params[i].name,
                           param_name_len) == 0)
              {
+               if (i == EMBED_PARAM_OFFSET
+                   && !CPP_OPTION (pfile, cplusplus))
+                 continue;
                param_kind = i;
                break;
              }
@@ -1285,6 +1290,7 @@ _cpp_parse_embed_params (cpp_reader *pfi
        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 +1300,28 @@ _cpp_parse_embed_params (cpp_reader *pfi
            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;
            }

        Jakub

Reply via email to