]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement C++26 P2361R6 - Unevaluated strings [PR110342]
authorJakub Jelinek <jakub@redhat.com>
Thu, 2 Nov 2023 06:44:24 +0000 (07:44 +0100)
committerJakub Jelinek <jakub@redhat.com>
Thu, 2 Nov 2023 06:44:24 +0000 (07:44 +0100)
The following patch implements C++26 unevaluated-string.
As it seems to me just extra pedanticity, it is implemented only for
-std=c++26 or -std=gnu++26 and later and only if -pedantic/-pedantic-errors.
Nothing is done for inline asm, while the spec changes those, it changes it
to a balanced token sequence with implementation defined rules on what is
and isn't allowed (so pedantically accepting asm ("" : "+m" (x));
was accepts-invalid before C++26, but we didn't diagnose anything).
For the other spots mentioned in the paper, static_assert message,
linkage specification, deprecated/nodiscard attributes it enforces the
requirements (no prefixes, udlit suffixes, no octal/hexadecimal escapes
(conditional escape sequences were rejected with pedantic already before).
For the deprecated operator "" identifier case I've kept things as is,
because everything seems to have been diagnosed already (a lot being implied
from the string having to be empty).

2023-11-02  Jakub Jelinek  <jakub@redhat.com>

PR c++/110342
gcc/cp/
* parser.cc: Implement C++26 P2361R6 - Unevaluated strings.
(uneval_string_attr): New enumerator.
(cp_parser_string_literal_common): Add UNEVAL argument.  If true,
pass CPP_UNEVAL_STRING rather than CPP_STRING to
cpp_interpret_string_notranslate.
(cp_parser_string_literal, cp_parser_userdef_string_literal): Adjust
callers of cp_parser_string_literal_common.
(cp_parser_unevaluated_string_literal): New function.
(cp_parser_parenthesized_expression_list): Handle uneval_string_attr.
(cp_parser_linkage_specification): Use
cp_parser_unevaluated_string_literal for C++26.
(cp_parser_static_assert): Likewise.
(cp_parser_std_attribute): Use uneval_string_attr for standard
deprecated and nodiscard attributes.
gcc/testsuite/
* g++.dg/cpp26/unevalstr1.C: New test.
* g++.dg/cpp26/unevalstr2.C: New test.
* g++.dg/cpp0x/udlit-error1.C (lol): Expect an error for C++26
about user-defined literal in deprecated attribute.
libcpp/
* include/cpplib.h (TTYPE_TABLE): Add CPP_UNEVAL_STRING literal
entry.  Use C++11 instead of C++-0x in comments.
* charset.cc (convert_escape): Add UNEVAL argument, if true,
pedantically diagnose numeric escape sequences.
(cpp_interpret_string_1): Formatting fix.  Adjust convert_escape
caller.
(cpp_interpret_string): Formatting string.
(cpp_interpret_string_notranslate): Pass type through to
cpp_interpret_string if it is CPP_UNEVAL_STRING.

gcc/cp/parser.cc
gcc/testsuite/g++.dg/cpp0x/udlit-error1.C
gcc/testsuite/g++.dg/cpp26/unevalstr1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/unevalstr2.C [new file with mode: 0644]
libcpp/charset.cc
libcpp/include/cpplib.h

index 5f1390435ccf67407d5cfb08e15a22ab50ed8122..20e1836590653a666a0cddd12442279f6a88ad06 100644 (file)
@@ -2324,7 +2324,8 @@ static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
   (cp_parser *, int, bool, bool, bool *, location_t * = NULL,
    bool = false);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
-enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3 };
+enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3,
+       uneval_string_attr = 4 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
 static cp_expr cp_parser_unary_expression
@@ -4467,7 +4468,8 @@ cp_parser_identifier (cp_parser* parser)
     return error_mark_node;
 }
 
-/* Worker for cp_parser_string_literal and cp_parser_userdef_string_literal.
+/* Worker for cp_parser_string_literal, cp_parser_userdef_string_literal
+   and cp_parser_unevaluated_string_literal.
    Do not call this directly; use either of the above.
 
    Parse a sequence of adjacent string constants.  Return a
@@ -4475,7 +4477,8 @@ cp_parser_identifier (cp_parser* parser)
    constant.  If TRANSLATE is true, translate the string to the
    execution character set.  If WIDE_OK is true, a wide string is
    valid here.  If UDL_OK is true, a string literal with user-defined
-   suffix can be used in this context.
+   suffix can be used in this context.  If UNEVAL is true, diagnose
+   numeric and conditional escape sequences in it if pedantic.
 
    C++98 [lex.string] says that if a narrow string literal token is
    adjacent to a wide string literal token, the behavior is undefined.
@@ -4489,7 +4492,7 @@ cp_parser_identifier (cp_parser* parser)
 static cp_expr
 cp_parser_string_literal_common (cp_parser *parser, bool translate,
                                 bool wide_ok, bool udl_ok,
-                                bool lookup_udlit)
+                                bool lookup_udlit, bool uneval)
 {
   tree value;
   size_t count;
@@ -4642,6 +4645,8 @@ cp_parser_string_literal_common (cp_parser *parser, bool translate,
       cp_parser_error (parser, "a wide string is invalid in this context");
       type = CPP_STRING;
     }
+  if (uneval)
+    type = CPP_UNEVAL_STRING;
 
   if ((translate ? cpp_interpret_string : cpp_interpret_string_notranslate)
       (parse_in, strs, count, &istr, type))
@@ -4716,7 +4721,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok)
 {
   return cp_parser_string_literal_common (parser, translate, wide_ok,
                                          /*udl_ok=*/false,
-                                         /*lookup_udlit=*/false);
+                                         /*lookup_udlit=*/false,
+                                         /*uneval=*/false);
 }
 
 /* Parse a string literal or user defined string literal.
@@ -4731,7 +4737,21 @@ cp_parser_userdef_string_literal (cp_parser *parser, bool lookup_udlit)
 {
   return cp_parser_string_literal_common (parser, /*translate=*/true,
                                          /*wide_ok=*/true, /*udl_ok=*/true,
-                                         lookup_udlit);
+                                         lookup_udlit, /*uneval=*/false);
+}
+
+/* Parse an unevaluated string literal.
+
+   unevaluated-string:
+     string-literal  */
+
+static inline cp_expr
+cp_parser_unevaluated_string_literal (cp_parser *parser)
+{
+  return cp_parser_string_literal_common (parser, /*translate=*/false,
+                                         /*wide_ok=*/false, /*udl_ok=*/false,
+                                         /*lookup_udlit=*/false,
+                                         /*uneval=*/true);
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -8658,6 +8678,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
          expr = cp_lexer_consume_token (parser->lexer)->u.value;
        else if (is_attribute_list == assume_attr)
          expr = cp_parser_conditional_expression (parser);
+       else if (is_attribute_list == uneval_string_attr)
+         expr = cp_parser_unevaluated_string_literal (parser);
        else
          expr
            = cp_parser_parenthesized_expression_list_elt (parser, cast_p,
@@ -16513,8 +16535,12 @@ cp_parser_linkage_specification (cp_parser* parser, tree prefix_attr)
 
   /* Look for the string-literal.  */
   cp_token *string_token = cp_lexer_peek_token (parser->lexer);
-  tree linkage = cp_parser_string_literal (parser, /*translate=*/false,
-                                          /*wide_ok=*/false);
+  tree linkage;
+  if (cxx_dialect >= cxx26)
+    linkage = cp_parser_unevaluated_string_literal (parser);
+  else
+    linkage = cp_parser_string_literal (parser, /*translate=*/false,
+                                       /*wide_ok=*/false);
 
   /* Transform the literal into an identifier.  If the literal is a
      wide-character string, or contains embedded NULs, then we can't
@@ -16643,8 +16669,11 @@ cp_parser_static_assert (cp_parser *parser, bool member_p)
       cp_parser_require (parser, CPP_COMMA, RT_COMMA);
 
       /* Parse the string-literal message.  */
-      message = cp_parser_string_literal (parser, /*translate=*/false,
-                                         /*wide_ok=*/true);
+      if (cxx_dialect >= cxx26)
+       message = cp_parser_unevaluated_string_literal (parser);
+      else
+       message = cp_parser_string_literal (parser, /*translate=*/false,
+                                           /*wide_ok=*/true);
 
       /* A `)' completes the static assertion.  */
       if (!parens.require_close (parser))
@@ -29650,6 +29679,11 @@ cp_parser_std_attribute (cp_parser *parser, tree attr_ns)
             && attribute_takes_identifier_p (attr_id))
       /* A GNU attribute that takes an identifier in parameter.  */
       attr_flag = id_attr;
+    else if (attr_ns == NULL_TREE
+            && cxx_dialect >= cxx26
+            && (is_attribute_p ("deprecated", attr_id)
+                || is_attribute_p ("nodiscard", attr_id)))
+      attr_flag = uneval_string_attr;
 
     /* If this is a fake attribute created to handle -Wno-attributes,
        we must skip parsing the arguments.  */
index 66e300e350f11cb154cf9db83c319415d30c7954..40566e0572f31de5ad7750f4075488ae3065285b 100644 (file)
@@ -13,7 +13,7 @@ void operator""_x(const char *, decltype(sizeof(0)));
 extern "C"_x { void g(); } // { dg-error "before user-defined string literal" }
 static_assert(true, "foo"_x); // { dg-error "string literal with user-defined suffix is invalid in this context|expected" }
 
-[[deprecated("oof"_x)]]
+[[deprecated("oof"_x)]]        // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } }
 void
 lol () // { dg-error "not a string" }
 {
diff --git a/gcc/testsuite/g++.dg/cpp26/unevalstr1.C b/gcc/testsuite/g++.dg/cpp26/unevalstr1.C
new file mode 100644 (file)
index 0000000..3220cf0
--- /dev/null
@@ -0,0 +1,103 @@
+// C++26 P2361R6 - Unevaluated strings
+// { dg-do compile { target c++26 } }
+
+static_assert (true, "foo");
+static_assert (true, "foo" " " "bar");
+static_assert (true, "\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v");
+static_assert (true, L"foo");          // { dg-error "a wide string is invalid in this context" }
+static_assert (true, u"foo");          // { dg-error "a wide string is invalid in this context" }
+static_assert (true, U"foo");          // { dg-error "a wide string is invalid in this context" }
+static_assert (true, u8"foo");         // { dg-error "a wide string is invalid in this context" }
+static_assert (true, L"fo" "o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, u"fo" "o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, U"fo" "o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, u8"fo" "o");      // { dg-error "a wide string is invalid in this context" }
+static_assert (true, "fo" L"o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, "fo" u"o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, "fo" U"o");       // { dg-error "a wide string is invalid in this context" }
+static_assert (true, "fo" u8"o");      // { dg-error "a wide string is invalid in this context" }
+static_assert (true, "\0");            // { dg-error "numeric escape sequence in unevaluated string" }
+static_assert (true, "\17");           // { dg-error "numeric escape sequence in unevaluated string" }
+static_assert (true, "\x20");          // { dg-error "numeric escape sequence in unevaluated string" }
+static_assert (true, "\o{17}");                // { dg-error "numeric escape sequence in unevaluated string" }
+static_assert (true, "\x{20}");                // { dg-error "numeric escape sequence in unevaluated string" }
+static_assert (true, "\h");            // { dg-error "unknown escape sequence" }
+
+extern "C" "+" "+" int f0 ();
+extern "C" int f1 ();
+extern "C" { int f2 (); };
+extern L"C" int f3 ();                 // { dg-error "a wide string is invalid in this context" }
+extern L"C" { int f4 (); }             // { dg-error "a wide string is invalid in this context" }
+extern u"C" int f5 ();                 // { dg-error "a wide string is invalid in this context" }
+extern u"C" { int f6 (); }             // { dg-error "a wide string is invalid in this context" }
+extern U"C" int f7 ();                 // { dg-error "a wide string is invalid in this context" }
+extern U"C" { int f8 (); }             // { dg-error "a wide string is invalid in this context" }
+extern u8"C" int f9 ();                        // { dg-error "a wide string is invalid in this context" }
+extern u8"C" { int f10 (); }           // { dg-error "a wide string is invalid in this context" }
+extern "\x43" int f11 ();              // { dg-error "numeric escape sequence in unevaluated string" }
+extern "\x{43}" { int f12 (); }                // { dg-error "numeric escape sequence in unevaluated string" }
+extern "\103" int f13 ();              // { dg-error "numeric escape sequence in unevaluated string" }
+extern "\o{0103}" { int f14 (); }      // { dg-error "numeric escape sequence in unevaluated string" }
+
+[[deprecated ("foo")]] int g0 ();
+[[deprecated ("foo" " " "bar")]] int g1 ();
+[[deprecated ("\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v")]] int g2 ();
+[[deprecated (L"foo")]] int g3 ();     // { dg-error "a wide string is invalid in this context" }
+[[deprecated (u"foo")]] int g4 ();     // { dg-error "a wide string is invalid in this context" }
+[[deprecated (U"foo")]] int g5 ();     // { dg-error "a wide string is invalid in this context" }
+[[deprecated (u8"foo")]] int g6 ();    // { dg-error "a wide string is invalid in this context" }
+[[deprecated (L"fo" "o")]] int g7 ();  // { dg-error "a wide string is invalid in this context" }
+[[deprecated (u"fo" "o")]] int g8 ();  // { dg-error "a wide string is invalid in this context" }
+[[deprecated (U"fo" "o")]] int g9 ();  // { dg-error "a wide string is invalid in this context" }
+[[deprecated (u8"fo" "o")]] int g10 ();        // { dg-error "a wide string is invalid in this context" }
+[[deprecated ("fo" L"o")]] int g11 (); // { dg-error "a wide string is invalid in this context" }
+[[deprecated ("fo" u"o")]] int g12 (); // { dg-error "a wide string is invalid in this context" }
+[[deprecated ("fo" U"o")]] int g13 (); // { dg-error "a wide string is invalid in this context" }
+[[deprecated ("fo" u8"o")]] int g14 ();        // { dg-error "a wide string is invalid in this context" }
+[[deprecated ("\0")]] int g15 ();      // { dg-error "numeric escape sequence in unevaluated string" }
+[[deprecated ("\17")]] int g16 ();     // { dg-error "numeric escape sequence in unevaluated string" }
+[[deprecated ("\x20")]] int g17 ();    // { dg-error "numeric escape sequence in unevaluated string" }
+[[deprecated ("\o{17}")]] int g18 ();  // { dg-error "numeric escape sequence in unevaluated string" }
+[[deprecated ("\x{20}")]] int g19 ();  // { dg-error "numeric escape sequence in unevaluated string" }
+[[deprecated ("\h")]] int g20 ();      // { dg-error "unknown escape sequence" }
+
+[[nodiscard ("foo")]] int h0 ();
+[[nodiscard ("foo" " " "bar")]] int h1 ();
+[[nodiscard ("\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v")]] int h2 ();
+[[nodiscard (L"foo")]] int h3 ();      // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (u"foo")]] int h4 ();      // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (U"foo")]] int h5 ();      // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (u8"foo")]] int h6 ();     // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (L"fo" "o")]] int h7 ();   // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (u"fo" "o")]] int h8 ();   // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (U"fo" "o")]] int h9 ();   // { dg-error "a wide string is invalid in this context" }
+[[nodiscard (u8"fo" "o")]] int h10 (); // { dg-error "a wide string is invalid in this context" }
+[[nodiscard ("fo" L"o")]] int h11 ();  // { dg-error "a wide string is invalid in this context" }
+[[nodiscard ("fo" u"o")]] int h12 ();  // { dg-error "a wide string is invalid in this context" }
+[[nodiscard ("fo" U"o")]] int h13 ();  // { dg-error "a wide string is invalid in this context" }
+[[nodiscard ("fo" u8"o")]] int h14 (); // { dg-error "a wide string is invalid in this context" }
+[[nodiscard ("\0")]] int h15 ();       // { dg-error "numeric escape sequence in unevaluated string" }
+[[nodiscard ("\17")]] int h16 ();      // { dg-error "numeric escape sequence in unevaluated string" }
+[[nodiscard ("\x20")]] int h17 ();     // { dg-error "numeric escape sequence in unevaluated string" }
+[[nodiscard ("\o{17}")]] int h18 ();   // { dg-error "numeric escape sequence in unevaluated string" }
+[[nodiscard ("\x{20}")]] int h19 ();   // { dg-error "numeric escape sequence in unevaluated string" }
+[[nodiscard ("\h")]] int h20 ();       // { dg-error "unknown escape sequence" }
+
+float operator "" _my0 (const char *);
+float operator "" "" _my1 (const char *);
+float operator L"" _my2 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator u"" _my3 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator U"" _my4 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator u8"" _my5 (const char *);       // { dg-error "invalid encoding prefix in literal operator" }
+float operator L"" "" _my6 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator u"" "" _my7 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator U"" "" _my8 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator u8"" "" _my9 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" L"" _my10 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" u"" _my11 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" U"" _my12 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" u8"" _my13 (const char *);   // { dg-error "invalid encoding prefix in literal operator" }
+float operator "\0" _my14 (const char *);      // { dg-error "expected empty string after 'operator' keyword" }
+float operator "\x00" _my15 (const char *);    // { dg-error "expected empty string after 'operator' keyword" }
+float operator "\h" _my16 (const char *);      // { dg-error "expected empty string after 'operator' keyword" }
+                                               // { dg-error "unknown escape sequence" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp26/unevalstr2.C b/gcc/testsuite/g++.dg/cpp26/unevalstr2.C
new file mode 100644 (file)
index 0000000..f719f25
--- /dev/null
@@ -0,0 +1,110 @@
+// C++26 P2361R6 - Unevaluated strings
+// { dg-do compile { target { c++11 && c++23_down } } }
+// { dg-options "-pedantic" }
+
+static_assert (true, "foo");
+static_assert (true, "foo" " " "bar");
+static_assert (true, "\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v");
+// { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } .-1 }
+// { dg-warning "named universal character escapes are only valid in" "" { target c++20_down } .-2 }
+static_assert (true, L"foo");
+static_assert (true, u"foo");
+static_assert (true, U"foo");
+static_assert (true, u8"foo");
+static_assert (true, L"fo" "o");
+static_assert (true, u"fo" "o");
+static_assert (true, U"fo" "o");
+static_assert (true, u8"fo" "o");
+static_assert (true, "fo" L"o");
+static_assert (true, "fo" u"o");
+static_assert (true, "fo" U"o");
+static_assert (true, "fo" u8"o");
+static_assert (true, "\0");
+static_assert (true, "\17");
+static_assert (true, "\x20");
+static_assert (true, "\o{17}");                // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+static_assert (true, "\x{20}");                // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+static_assert (true, "\h");            // { dg-warning "unknown escape sequence" }
+
+extern "C" "+" "+" int f0 ();
+extern "C" int f1 ();
+extern "C" { int f2 (); };
+extern L"C" int f3 ();                 // { dg-error "a wide string is invalid in this context" }
+extern L"C" { int f4 (); }             // { dg-error "a wide string is invalid in this context" }
+extern u"C" int f5 ();                 // { dg-error "a wide string is invalid in this context" }
+extern u"C" { int f6 (); }             // { dg-error "a wide string is invalid in this context" }
+extern U"C" int f7 ();                 // { dg-error "a wide string is invalid in this context" }
+extern U"C" { int f8 (); }             // { dg-error "a wide string is invalid in this context" }
+extern u8"C" int f9 ();                        // { dg-error "a wide string is invalid in this context" }
+extern u8"C" { int f10 (); }           // { dg-error "a wide string is invalid in this context" }
+extern "\x43" int f11 ();
+extern "\x{43}" { int f12 (); }                // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+extern "\103" int f13 ();
+extern "\o{0103}" { int f14 (); }      // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+
+[[deprecated ("foo")]] int g0 ();
+[[deprecated ("foo" " " "bar")]] int g1 ();
+[[deprecated ("\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v")]] int g2 ();
+// { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } .-1 }
+// { dg-warning "named universal character escapes are only valid in" "" { target c++20_down } .-2 }
+[[deprecated (L"foo")]] int g3 ();
+[[deprecated (u"foo")]] int g4 ();
+[[deprecated (U"foo")]] int g5 ();
+[[deprecated (u8"foo")]] int g6 ();
+[[deprecated (L"fo" "o")]] int g7 ();
+[[deprecated (u"fo" "o")]] int g8 ();
+[[deprecated (U"fo" "o")]] int g9 ();
+[[deprecated (u8"fo" "o")]] int g10 ();
+[[deprecated ("fo" L"o")]] int g11 ();
+[[deprecated ("fo" u"o")]] int g12 ();
+[[deprecated ("fo" U"o")]] int g13 ();
+[[deprecated ("fo" u8"o")]] int g14 ();
+[[deprecated ("\0")]] int g15 ();
+[[deprecated ("\17")]] int g16 ();
+[[deprecated ("\x20")]] int g17 ();
+[[deprecated ("\o{17}")]] int g18 ();  // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+[[deprecated ("\x{20}")]] int g19 ();  // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+[[deprecated ("\h")]] int g20 ();      // { dg-warning "unknown escape sequence" }
+
+[[nodiscard ("foo")]] int h0 ();
+[[nodiscard ("foo" " " "bar")]] int h1 ();
+[[nodiscard ("\u01FC\U000001FC\u{1FC}\N{LATIN CAPITAL LETTER AE WITH ACUTE}\\\'\"\?\a\b\f\n\r\t\v")]] int h2 ();
+// { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } .-1 }
+// { dg-warning "named universal character escapes are only valid in" "" { target c++20_down } .-2 }
+[[nodiscard (L"foo")]] int h3 ();
+[[nodiscard (u"foo")]] int h4 ();
+[[nodiscard (U"foo")]] int h5 ();
+[[nodiscard (u8"foo")]] int h6 ();
+[[nodiscard (L"fo" "o")]] int h7 ();
+[[nodiscard (u"fo" "o")]] int h8 ();
+[[nodiscard (U"fo" "o")]] int h9 ();
+[[nodiscard (u8"fo" "o")]] int h10 ();
+[[nodiscard ("fo" L"o")]] int h11 ();
+[[nodiscard ("fo" u"o")]] int h12 ();
+[[nodiscard ("fo" U"o")]] int h13 ();
+[[nodiscard ("fo" u8"o")]] int h14 ();
+[[nodiscard ("\0")]] int h15 ();
+[[nodiscard ("\17")]] int h16 ();
+[[nodiscard ("\x20")]] int h17 ();
+[[nodiscard ("\o{17}")]] int h18 ();   // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+[[nodiscard ("\x{20}")]] int h19 ();   // { dg-warning "delimited escape sequences are only valid in" "" { target c++20_down } }
+[[nodiscard ("\h")]] int h20 ();       // { dg-warning "unknown escape sequence" }
+
+float operator "" _my0 (const char *);
+float operator "" "" _my1 (const char *);
+float operator L"" _my2 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator u"" _my3 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator U"" _my4 (const char *);        // { dg-error "invalid encoding prefix in literal operator" }
+float operator u8"" _my5 (const char *);       // { dg-error "invalid encoding prefix in literal operator" }
+float operator L"" "" _my6 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator u"" "" _my7 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator U"" "" _my8 (const char *);     // { dg-error "invalid encoding prefix in literal operator" }
+float operator u8"" "" _my9 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" L"" _my10 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" u"" _my11 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" U"" _my12 (const char *);    // { dg-error "invalid encoding prefix in literal operator" }
+float operator "" u8"" _my13 (const char *);   // { dg-error "invalid encoding prefix in literal operator" "" { target c++20 } }
+float operator "\0" _my14 (const char *);      // { dg-error "expected empty string after 'operator' keyword" }
+float operator "\x00" _my15 (const char *);    // { dg-error "expected empty string after 'operator' keyword" }
+float operator "\h" _my16 (const char *);      // { dg-error "expected empty string after 'operator' keyword" }
+                                               // { dg-warning "unknown escape sequence" "" { target *-*-* } .-1 }
index 7b625c9956a62d82efb44ecf870c5bc90f55cebb..d5a027502cd6980831cb9a64a62b0b3c841c63ac 100644 (file)
@@ -2156,7 +2156,7 @@ static const uchar *
 convert_escape (cpp_reader *pfile, const uchar *from, const uchar *limit,
                struct _cpp_strbuf *tbuf, struct cset_converter cvt,
                cpp_string_location_reader *loc_reader,
-               cpp_substring_ranges *ranges)
+               cpp_substring_ranges *ranges, bool uneval)
 {
   /* Values of \a \b \e \f \n \r \t \v respectively.  */
 #if HOST_CHARSET == HOST_CHARSET_ASCII
@@ -2183,12 +2183,20 @@ convert_escape (cpp_reader *pfile, const uchar *from, const uchar *limit,
                          char_range, loc_reader, ranges);
 
     case 'x':
+      if (uneval && CPP_PEDANTIC (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "numeric escape sequence in unevaluated string: "
+                  "'\\%c'", (int) c);
       return convert_hex (pfile, from, limit, tbuf, cvt,
                          char_range, loc_reader, ranges);
 
     case '0':  case '1':  case '2':  case '3':
     case '4':  case '5':  case '6':  case '7':
     case 'o':
+      if (uneval && CPP_PEDANTIC (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "numeric escape sequence in unevaluated string: "
+                  "'\\%c'", (int) c);
       return convert_oct (pfile, from, limit, tbuf, cvt,
                          char_range, loc_reader, ranges);
 
@@ -2296,7 +2304,7 @@ converter_for_type (cpp_reader *pfile, enum cpp_ttype type)
 
 static bool
 cpp_interpret_string_1 (cpp_reader *pfile, const cpp_string *from, size_t count,
-                       cpp_string *to,  enum cpp_ttype type,
+                       cpp_string *to, enum cpp_ttype type,
                        cpp_string_location_reader *loc_readers,
                        cpp_substring_ranges *out)
 {
@@ -2427,7 +2435,7 @@ cpp_interpret_string_1 (cpp_reader *pfile, const cpp_string *from, size_t count,
 
          struct _cpp_strbuf *tbuf_ptr = to ? &tbuf : NULL;
          p = convert_escape (pfile, p + 1, limit, tbuf_ptr, cvt,
-                             loc_reader, out);
+                             loc_reader, out, type == CPP_UNEVAL_STRING);
        }
     }
 
@@ -2465,7 +2473,7 @@ cpp_interpret_string_1 (cpp_reader *pfile, const cpp_string *from, size_t count,
    false for failure.  */
 bool
 cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
-                     cpp_string *to,  enum cpp_ttype type)
+                     cpp_string *to, enum cpp_ttype type)
 {
   return cpp_interpret_string_1 (pfile, from, count, to, type, NULL, NULL);
 }
@@ -2548,7 +2556,7 @@ cpp_interpret_string_ranges (cpp_reader *pfile, const cpp_string *from,
 bool
 cpp_interpret_string_notranslate (cpp_reader *pfile, const cpp_string *from,
                                  size_t count, cpp_string *to,
-                                 enum cpp_ttype type ATTRIBUTE_UNUSED)
+                                 enum cpp_ttype type)
 {
   struct cset_converter save_narrow_cset_desc = pfile->narrow_cset_desc;
   bool retval;
@@ -2557,7 +2565,9 @@ cpp_interpret_string_notranslate (cpp_reader *pfile, const cpp_string *from,
   pfile->narrow_cset_desc.cd = (iconv_t) -1;
   pfile->narrow_cset_desc.width = CPP_OPTION (pfile, char_precision);
 
-  retval = cpp_interpret_string (pfile, from, count, to, CPP_STRING);
+  retval = cpp_interpret_string (pfile, from, count, to,
+                                type == CPP_UNEVAL_STRING
+                                ? CPP_UNEVAL_STRING : CPP_STRING);
 
   pfile->narrow_cset_desc = save_narrow_cset_desc;
   return retval;
index fe73a27097622d691cb5aaf3b770faee8f0d9462..d873897bd3c4ef365a54b637ef922272799ac70c 100644 (file)
@@ -129,17 +129,18 @@ struct _cpp_file;
   TK(UTF8STRING,       LITERAL) /* u8"string" */                       \
   TK(OBJC_STRING,      LITERAL) /* @"string" - Objective-C */          \
   TK(HEADER_NAME,      LITERAL) /* <stdio.h> in #include */            \
+  TK(UNEVAL_STRING,    LITERAL) /* unevaluated "string" - C++26 */     \
                                                                        \
-  TK(CHAR_USERDEF,     LITERAL) /* 'char'_suffix - C++-0x */           \
-  TK(WCHAR_USERDEF,    LITERAL) /* L'char'_suffix - C++-0x */          \
-  TK(CHAR16_USERDEF,   LITERAL) /* u'char'_suffix - C++-0x */          \
-  TK(CHAR32_USERDEF,   LITERAL) /* U'char'_suffix - C++-0x */          \
-  TK(UTF8CHAR_USERDEF, LITERAL) /* u8'char'_suffix - C++-0x */         \
-  TK(STRING_USERDEF,   LITERAL) /* "string"_suffix - C++-0x */         \
-  TK(WSTRING_USERDEF,  LITERAL) /* L"string"_suffix - C++-0x */        \
-  TK(STRING16_USERDEF, LITERAL) /* u"string"_suffix - C++-0x */        \
-  TK(STRING32_USERDEF, LITERAL) /* U"string"_suffix - C++-0x */        \
-  TK(UTF8STRING_USERDEF,LITERAL) /* u8"string"_suffix - C++-0x */      \
+  TK(CHAR_USERDEF,     LITERAL) /* 'char'_suffix - C++11 */            \
+  TK(WCHAR_USERDEF,    LITERAL) /* L'char'_suffix - C++11 */           \
+  TK(CHAR16_USERDEF,   LITERAL) /* u'char'_suffix - C++11 */           \
+  TK(CHAR32_USERDEF,   LITERAL) /* U'char'_suffix - C++11 */           \
+  TK(UTF8CHAR_USERDEF, LITERAL) /* u8'char'_suffix - C++11 */          \
+  TK(STRING_USERDEF,   LITERAL) /* "string"_suffix - C++11 */          \
+  TK(WSTRING_USERDEF,  LITERAL) /* L"string"_suffix - C++11 */         \
+  TK(STRING16_USERDEF, LITERAL) /* u"string"_suffix - C++11 */         \
+  TK(STRING32_USERDEF, LITERAL) /* U"string"_suffix - C++11 */         \
+  TK(UTF8STRING_USERDEF,LITERAL) /* u8"string"_suffix - C++11 */       \
                                                                        \
   TK(COMMENT,          LITERAL) /* Only if output comments.  */        \
                                 /* SPELL_LITERAL happens to DTRT.  */  \