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,
