[clang] [Clang] Allow raw string literals in C as an extension (PR #88265)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 22 11:13:47 PDT 2024
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/88265
>From 40e533e6f58acbe832b3fa4e14ca9fd600cf77cf Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 10 Apr 2024 14:36:23 +0200
Subject: [PATCH 1/2] [Clang] Allow raw string literals in C as an extension
---
clang/docs/ReleaseNotes.rst | 3 +++
clang/include/clang/Basic/LangOptions.def | 2 ++
clang/include/clang/Basic/LangStandard.h | 6 ++++++
clang/include/clang/Driver/Options.td | 6 ++++++
clang/lib/Basic/LangOptions.cpp | 1 +
clang/lib/Driver/ToolChains/Clang.cpp | 2 ++
clang/lib/Format/Format.cpp | 1 +
clang/lib/Lex/Lexer.cpp | 10 +++++-----
clang/test/Lexer/raw-string-ext.c | 18 ++++++++++++++++++
9 files changed, 44 insertions(+), 5 deletions(-)
create mode 100644 clang/test/Lexer/raw-string-ext.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f96cebbde3d825..20d14130fb62bc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -43,6 +43,9 @@ code bases.
C/C++ Language Potentially Breaking Changes
-------------------------------------------
+- Clang now supports raw string literals in ``-std=gnuXY`` mode as an extension in
+ C. This behaviour can also be overridden using ``-f[no-]raw-string-literals``.
+
C++ Specific Potentially Breaking Changes
-----------------------------------------
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 8ef6700ecdc78e..96bd339bb1851d 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -454,6 +454,8 @@ LANGOPT(MatrixTypes, 1, 0, "Enable or disable the builtin matrix type")
LANGOPT(CXXAssumptions, 1, 1, "Enable or disable codegen and compile-time checks for C++23's [[assume]] attribute")
+LANGOPT(RawStringLiterals, 1, 0, "Enable or disable raw string literals")
+
ENUM_LANGOPT(StrictFlexArraysLevel, StrictFlexArraysLevelKind, 2,
StrictFlexArraysLevelKind::Default,
"Rely on strict definition of flexible arrays")
diff --git a/clang/include/clang/Basic/LangStandard.h b/clang/include/clang/Basic/LangStandard.h
index 8e25afc833661c..0a308b93ada746 100644
--- a/clang/include/clang/Basic/LangStandard.h
+++ b/clang/include/clang/Basic/LangStandard.h
@@ -130,6 +130,12 @@ struct LangStandard {
/// hasDigraphs - Language supports digraphs.
bool hasDigraphs() const { return Flags & Digraphs; }
+ /// hasRawStringLiterals - Language supports R"()" raw string literals.
+ bool hasRawStringLiterals() const {
+ // GCC supports raw string literals in C, but not in C++ before C++11.
+ return isCPlusPlus11() || (!isCPlusPlus() && isGNUMode());
+ }
+
/// isGNUMode - Language includes GNU extensions.
bool isGNUMode() const { return Flags & GNUMode; }
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f745e573eb2686..32e6c10e1251b7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4142,6 +4142,12 @@ def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>,
HelpText<"Enable matrix data type and related builtin functions">,
MarshallingInfoFlag<LangOpts<"MatrixTypes">>;
+defm raw_string_literals : BoolFOption<"raw-string-literals",
+ LangOpts<"RawStringLiterals">, Default<std#".hasRawStringLiterals()">,
+ PosFlag<SetTrue, [], [], "Enable">,
+ NegFlag<SetFalse, [], [], "Disable">,
+ BothFlags<[], [ClangOption, CC1Option], " raw string literals">>;
+
def fzero_call_used_regs_EQ
: Joined<["-"], "fzero-call-used-regs=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/Basic/LangOptions.cpp b/clang/lib/Basic/LangOptions.cpp
index a0adfbf61840e3..c34f0ed5ed7174 100644
--- a/clang/lib/Basic/LangOptions.cpp
+++ b/clang/lib/Basic/LangOptions.cpp
@@ -124,6 +124,7 @@ void LangOptions::setLangDefaults(LangOptions &Opts, Language Lang,
Opts.HexFloats = Std.hasHexFloats();
Opts.WChar = Std.isCPlusPlus();
Opts.Digraphs = Std.hasDigraphs();
+ Opts.RawStringLiterals = Std.hasRawStringLiterals();
Opts.HLSL = Lang == Language::HLSL;
if (Opts.HLSL && Opts.IncludeDefaultHeader)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 766a9b91e3c0ad..c99bfe4efc4137 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6536,6 +6536,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fheinous_gnu_extensions);
Args.AddLastArg(CmdArgs, options::OPT_fdigraphs, options::OPT_fno_digraphs);
Args.AddLastArg(CmdArgs, options::OPT_fzero_call_used_regs_EQ);
+ Args.AddLastArg(CmdArgs, options::OPT_fraw_string_literals,
+ options::OPT_fno_raw_string_literals);
if (Args.hasFlag(options::OPT_femulated_tls, options::OPT_fno_emulated_tls,
Triple.hasDefaultEmulatedTLS()))
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 89e6c19b0af45c..71865bb061f57e 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -3850,6 +3850,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) {
// the sequence "<::" will be unconditionally treated as "[:".
// Cf. Lexer::LexTokenInternal.
LangOpts.Digraphs = LexingStd >= FormatStyle::LS_Cpp11;
+ LangOpts.RawStringLiterals = LexingStd >= FormatStyle::LS_Cpp11;
LangOpts.LineComment = 1;
bool AlternativeOperators = Style.isCpp();
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index c98645993abe07..67d75c1140b232 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3867,7 +3867,7 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
tok::utf16_char_constant);
// UTF-16 raw string literal
- if (Char == 'R' && LangOpts.CPlusPlus11 &&
+ if (Char == 'R' && LangOpts.RawStringLiterals &&
getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == '"')
return LexRawStringLiteral(Result,
ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result),
@@ -3889,7 +3889,7 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
SizeTmp2, Result),
tok::utf8_char_constant);
- if (Char2 == 'R' && LangOpts.CPlusPlus11) {
+ if (Char2 == 'R' && LangOpts.RawStringLiterals) {
unsigned SizeTmp3;
char Char3 = getCharAndSize(CurPtr + SizeTmp + SizeTmp2, SizeTmp3);
// UTF-8 raw string literal
@@ -3925,7 +3925,7 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
tok::utf32_char_constant);
// UTF-32 raw string literal
- if (Char == 'R' && LangOpts.CPlusPlus11 &&
+ if (Char == 'R' && LangOpts.RawStringLiterals &&
getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == '"')
return LexRawStringLiteral(Result,
ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result),
@@ -3940,7 +3940,7 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
// Notify MIOpt that we read a non-whitespace/non-comment token.
MIOpt.ReadToken();
- if (LangOpts.CPlusPlus11) {
+ if (LangOpts.RawStringLiterals) {
Char = getCharAndSize(CurPtr, SizeTmp);
if (Char == '"')
@@ -3963,7 +3963,7 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
tok::wide_string_literal);
// Wide raw string literal.
- if (LangOpts.CPlusPlus11 && Char == 'R' &&
+ if (LangOpts.RawStringLiterals && Char == 'R' &&
getCharAndSize(CurPtr + SizeTmp, SizeTmp2) == '"')
return LexRawStringLiteral(Result,
ConsumeChar(ConsumeChar(CurPtr, SizeTmp, Result),
diff --git a/clang/test/Lexer/raw-string-ext.c b/clang/test/Lexer/raw-string-ext.c
new file mode 100644
index 00000000000000..45e3990cadf3d2
--- /dev/null
+++ b/clang/test/Lexer/raw-string-ext.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -verify=gnu -DGNU %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -fraw-string-literals -verify=gnu -DGNU %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -verify=std %s
+// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -fno-raw-string-literals -verify=std %s
+
+void f() {
+ (void) R"foo()foo"; // std-error {{use of undeclared identifier 'R'}}
+ (void) LR"foo()foo"; // std-error {{use of undeclared identifier 'LR'}}
+ (void) uR"foo()foo"; // std-error {{use of undeclared identifier 'uR'}}
+ (void) u8R"foo()foo"; // std-error {{use of undeclared identifier 'u8R'}}
+ (void) UR"foo()foo"; // std-error {{use of undeclared identifier 'UR'}}
+}
+
+// gnu-error@* {{missing terminating delimiter}}
+// gnu-error@* {{expected expression}}
+// gnu-error@* {{expected ';' after top level declarator}}
+#define R "bar"
+const char* s = R"foo(";
>From f4f2294e6050168223d52a03e97f512ddab54764 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 22 Apr 2024 20:13:03 +0200
Subject: [PATCH 2/2] [Clang] Ignore '-f[no-]raw-string-literals' in C++ mode
---
clang/docs/ReleaseNotes.rst | 3 +-
.../clang/Basic/DiagnosticDriverKinds.td | 2 +
clang/include/clang/Basic/LangStandard.h | 5 ++-
clang/lib/Frontend/CompilerInvocation.cpp | 9 ++++
.../test/Driver/fraw-string-literals-cxx.cpp | 11 +++++
clang/test/Lexer/raw-string-ext.c | 45 ++++++++++++++-----
6 files changed, 60 insertions(+), 15 deletions(-)
create mode 100644 clang/test/Driver/fraw-string-literals-cxx.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 20d14130fb62bc..e98c0cde2e6b3f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -44,7 +44,8 @@ C/C++ Language Potentially Breaking Changes
-------------------------------------------
- Clang now supports raw string literals in ``-std=gnuXY`` mode as an extension in
- C. This behaviour can also be overridden using ``-f[no-]raw-string-literals``.
+ C99 and later. This behaviour can also be overridden using ``-f[no-]raw-string-literals``.
+ Support of raw string literals in C++ is not affected. Fixes (#GH85703).
C++ Specific Potentially Breaking Changes
-----------------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ed3fd9b1c4a55b..6185dd4691d8ac 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -375,6 +375,8 @@ def err_drv_negative_columns : Error<
"invalid value '%1' in '%0', value must be 'none' or a positive integer">;
def err_drv_small_columns : Error<
"invalid value '%1' in '%0', value must be '%2' or greater">;
+def warn_drv_fraw_string_literals_in_cxx : Warning<
+ "ignoring '-f%select{no-|}0raw-string-literals', which is only valid for C">;
def err_drv_invalid_malign_branch_EQ : Error<
"invalid argument '%0' to -malign-branch=; each element must be one of: %1">;
diff --git a/clang/include/clang/Basic/LangStandard.h b/clang/include/clang/Basic/LangStandard.h
index 0a308b93ada746..49594a49e9f37c 100644
--- a/clang/include/clang/Basic/LangStandard.h
+++ b/clang/include/clang/Basic/LangStandard.h
@@ -132,8 +132,9 @@ struct LangStandard {
/// hasRawStringLiterals - Language supports R"()" raw string literals.
bool hasRawStringLiterals() const {
- // GCC supports raw string literals in C, but not in C++ before C++11.
- return isCPlusPlus11() || (!isCPlusPlus() && isGNUMode());
+ // GCC supports raw string literals in C99 and later, but not in C++
+ // before C++11.
+ return isCPlusPlus11() || (!isCPlusPlus() && isC99() && isGNUMode());
}
/// isGNUMode - Language includes GNU extensions.
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 1f1f5440ddd75f..963f1d1bc34f55 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -610,6 +610,15 @@ static bool FixupInvocation(CompilerInvocation &Invocation,
LangOpts.NewAlignOverride = 0;
}
+ // The -f[no-]raw-string-literals option is only valid in C.
+ if ((Args.hasArg(OPT_fraw_string_literals) ||
+ Args.hasArg(OPT_fno_raw_string_literals)) &&
+ LangOpts.CPlusPlus) {
+ Diags.Report(diag::warn_drv_fraw_string_literals_in_cxx)
+ << bool(LangOpts.RawStringLiterals);
+ LangOpts.RawStringLiterals = LangOpts.CPlusPlus11;
+ }
+
// Prevent the user from specifying both -fsycl-is-device and -fsycl-is-host.
if (LangOpts.SYCLIsDevice && LangOpts.SYCLIsHost)
Diags.Report(diag::err_drv_argument_not_allowed_with) << "-fsycl-is-device"
diff --git a/clang/test/Driver/fraw-string-literals-cxx.cpp b/clang/test/Driver/fraw-string-literals-cxx.cpp
new file mode 100644
index 00000000000000..cb1133711a030a
--- /dev/null
+++ b/clang/test/Driver/fraw-string-literals-cxx.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang -fraw-string-literals -fsyntax-only -std=c++03 %s 2>&1 | FileCheck --check-prefix=CHECK-POS %s
+// RUN: %clang -fraw-string-literals -fsyntax-only -std=gnu++03 %s 2>&1 | FileCheck --check-prefix=CHECK-POS %s
+// RUN: %clang -fno-raw-string-literals -fsyntax-only -std=c++03 %s 2>&1 | FileCheck --check-prefix=CHECK-NEG %s
+// RUN: %clang -fno-raw-string-literals -fsyntax-only -std=gnu++03 %s 2>&1 | FileCheck --check-prefix=CHECK-NEG %s
+// RUN: %clang -fraw-string-literals -fsyntax-only -std=c++11 %s 2>&1 | FileCheck --check-prefix=CHECK-POS %s
+// RUN: %clang -fraw-string-literals -fsyntax-only -std=gnu++11 %s 2>&1 | FileCheck --check-prefix=CHECK-POS %s
+// RUN: %clang -fno-raw-string-literals -fsyntax-only -std=c++11 %s 2>&1 | FileCheck --check-prefix=CHECK-NEG %s
+// RUN: %clang -fno-raw-string-literals -fsyntax-only -std=gnu++11 %s 2>&1 | FileCheck --check-prefix=CHECK-NEG %s
+
+// CHECK-POS: ignoring '-fraw-string-literals', which is only valid for C
+// CHECK-NEG: ignoring '-fno-raw-string-literals', which is only valid for C
diff --git a/clang/test/Lexer/raw-string-ext.c b/clang/test/Lexer/raw-string-ext.c
index 45e3990cadf3d2..feac8d0c4beaf2 100644
--- a/clang/test/Lexer/raw-string-ext.c
+++ b/clang/test/Lexer/raw-string-ext.c
@@ -1,18 +1,39 @@
-// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -verify=gnu -DGNU %s
-// RUN: %clang_cc1 -fsyntax-only -std=c11 -fraw-string-literals -verify=gnu -DGNU %s
-// RUN: %clang_cc1 -fsyntax-only -std=c11 -verify=std %s
-// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -fno-raw-string-literals -verify=std %s
+// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -verify=supported %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -fraw-string-literals -verify=supported %s
+// RUN: %clang_cc1 -fsyntax-only -std=gnu89 -verify=unsupported %s
+// RUN: %clang_cc1 -fsyntax-only -std=c11 -verify=unsupported %s
+// RUN: %clang_cc1 -fsyntax-only -std=gnu11 -fno-raw-string-literals -verify=unsupported %s
+
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=c++03 -verify=unsupported,cxx-unsupported %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=gnu++03 -verify=unsupported,cxx-unsupported %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=c++03 -fraw-string-literals -verify=unsupported,cxx-unsupported,yes %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=gnu++03 -fraw-string-literals -verify=unsupported,cxx-unsupported,yes %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=c++11 -verify=supported,cxx %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=gnu++11 -verify=supported,cxx %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=c++11 -fno-raw-string-literals -verify=supported,no %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wno-unused -std=gnu++11 -fno-raw-string-literals -verify=supported,no %s
+
+// GCC supports raw string literals in C99 and later in '-std=gnuXY' mode; we
+// additionally provide '-f[no-]raw-string-literals' to enable/disable them
+// explicitly in C.
+//
+// We do not allow disabling or enabling raw string literals in C++ mode if
+// they’re not already enabled by the language standard.
+
+// Driver warnings.
+// yes-warning@* {{ignoring '-fraw-string-literals', which is only valid for C}}
+// no-warning@* {{ignoring '-fno-raw-string-literals', which is only valid for C}}
void f() {
- (void) R"foo()foo"; // std-error {{use of undeclared identifier 'R'}}
- (void) LR"foo()foo"; // std-error {{use of undeclared identifier 'LR'}}
- (void) uR"foo()foo"; // std-error {{use of undeclared identifier 'uR'}}
- (void) u8R"foo()foo"; // std-error {{use of undeclared identifier 'u8R'}}
- (void) UR"foo()foo"; // std-error {{use of undeclared identifier 'UR'}}
+ (void) R"foo()foo"; // unsupported-error {{use of undeclared identifier 'R'}} cxx-unsupported-error {{expected ';' after expression}}
+ (void) LR"foo()foo"; // unsupported-error {{use of undeclared identifier 'LR'}} cxx-unsupported-error {{expected ';' after expression}}
+ (void) uR"foo()foo"; // unsupported-error {{use of undeclared identifier 'uR'}} cxx-unsupported-error {{expected ';' after expression}}
+ (void) u8R"foo()foo"; // unsupported-error {{use of undeclared identifier 'u8R'}} cxx-unsupported-error {{expected ';' after expression}}
+ (void) UR"foo()foo"; // unsupported-error {{use of undeclared identifier 'UR'}} cxx-unsupported-error {{expected ';' after expression}}
}
-// gnu-error@* {{missing terminating delimiter}}
-// gnu-error@* {{expected expression}}
-// gnu-error@* {{expected ';' after top level declarator}}
+// supported-error@* {{missing terminating delimiter}}
+// supported-error@* {{expected expression}}
+// supported-error@* {{expected ';' after top level declarator}}
#define R "bar"
const char* s = R"foo(";
More information about the cfe-commits
mailing list