Skip to content

[Clang] Allow raw string literals in C as an extension #88265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 10, 2024
Merged
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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();``.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/LangStandard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/Lex/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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 == '"')
Expand All @@ -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),
Expand Down
18 changes: 18 additions & 0 deletions clang/test/Lexer/raw-string-ext.c
Original file line number Diff line number Diff line change
@@ -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(";