On Fri, Jun 12, 2026 at 06:47:15PM +0200, Jakub Jelinek wrote: > The following patch on top of the just posted -std=c++29 patch > implements the C++29 P3540R3 - #embed offset parameter paper.
I've missed the bump of the feature test macro in that paper, this updated version bumps it. Bootstrapped/regtested again on x86_64-linux and i686-linux, ok for trunk? 2026-06-29 Jakub Jelinek <[email protected]> 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++. --- 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/c-family/c-cppbuiltin.cc.jj 2026-06-12 16:55:01.601239410 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2026-06-25 19:25:44.680155968 +0200 @@ -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) --- gcc/testsuite/g++.dg/cpp29/feat-cxx29.C.jj 2026-06-25 10:03:50.869435729 +0200 +++ gcc/testsuite/g++.dg/cpp29/feat-cxx29.C 2026-06-25 19:26:30.103601323 +0200 @@ -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 --- 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,27 @@ _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
