-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[C2y] Add octal prefixes, deprecate unprefixed octals #131626
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
Conversation
WG14 N3353 added support for 0o and 0O as octal literal prefixes. It also deprecates use of octal literals without a prefix, except for the literal 0. This feature is being exposed as an extension in older C language modes as well as in all C++ language modes.
@llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesWG14 N3353 added support for 0o and 0O as octal literal prefixes. It also deprecates use of octal literals without a prefix, except for the literal 0. This feature is being exposed as an extension in older C language modes as well as in all C++ language modes. Full diff: https://github.com/llvm/llvm-project/pull/131626.diff 7 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index cc12ff5bad353..a96816916b5f2 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1652,6 +1652,7 @@ Designated initializers (N494) C
Array & element qualification (N2607) C23 C89
Attributes (N2335) C23 C89
``#embed`` (N3017) C23 C89, C++
+Octal literals prefixed with ``0o`` or ``0O`` C2y C89, C++
============================================= ================================ ============= =============
Builtin type aliases
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a1c5ee2d788e..bfb6235247709 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -120,6 +120,10 @@ C2y Feature Support
- Implemented `WG14 N3411 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3411.pdf>`_
which allows a source file to not end with a newline character. This is still
reported as a conforming extension in earlier language modes.
+- Implemented `WG14 N3353 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3353.htm>_`
+ which adds the new ``0o`` and ``0O`` ocal literal prefixes and deprecates
+ octal literals other than ``0`` which do not start with the new prefix. This
+ feature is exposed in earlier language modes and in C++ as an extension.
C23 Feature Support
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index fac80fb4009aa..e386d8f2c3b27 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -92,6 +92,7 @@ def EnumCompare : DiagGroup<"enum-compare", [EnumCompareSwitch,
def DeprecatedAnonEnumEnumConversion : DiagGroup<"deprecated-anon-enum-enum-conversion">;
def DeprecatedEnumEnumConversion : DiagGroup<"deprecated-enum-enum-conversion">;
def DeprecatedEnumFloatConversion : DiagGroup<"deprecated-enum-float-conversion">;
+def DeprecatedOctalLiterals : DiagGroup<"deprecated-octal-literals">;
def AnonEnumEnumConversion : DiagGroup<"anon-enum-enum-conversion",
[DeprecatedAnonEnumEnumConversion]>;
def EnumEnumConversion : DiagGroup<"enum-enum-conversion",
@@ -235,7 +236,8 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion,
DeprecatedVolatile,
DeprecatedWritableStr,
DeprecatedRedundantConstexprStaticDef,
- DeprecatedMissingCommaVariadicParam
+ DeprecatedMissingCommaVariadicParam,
+ DeprecatedOctalLiterals
]>,
DiagCategory<"Deprecations">;
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 0e5592d65669b..f662ad1211ba3 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -256,6 +256,14 @@ def warn_cxx17_hex_literal : Warning<
"hexadecimal floating literals are incompatible with "
"C++ standards before C++17">,
InGroup<CXXPre17CompatPedantic>, DefaultIgnore;
+def ext_octal_literal : Extension<
+ "octal integer literals are a C2y extension">, InGroup<C2y>;
+def warn_c2y_compat_octal_literal : Warning<
+ "octal integer literals are incompatible with standards before C2y">,
+ InGroup<CPre2yCompat>, DefaultIgnore;
+def warn_unprefixed_octal_deprecated : Warning<
+ "octal literals without a '0o' prefix are deprecated">,
+ InGroup<DeprecatedOctalLiterals>;
def ext_binary_literal : Extension<
"binary integer literals are a C23 extension">, InGroup<C23>;
def warn_c23_compat_binary_literal : Warning<
diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp
index 69dc057d0df4b..0ab27b2d40373 100644
--- a/clang/lib/Lex/LiteralSupport.cpp
+++ b/clang/lib/Lex/LiteralSupport.cpp
@@ -21,6 +21,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
@@ -1423,6 +1424,27 @@ void NumericLiteralParser::ParseNumberStartingWithZero(SourceLocation TokLoc) {
return;
}
+ // Parse a potential octal literal prefix.
+ bool SawOctalPrefix = false;
+ if ((c1 == 'O' || c1 == 'o') && (s[1] >= '0' && s[1] <= '7')) {
+ unsigned DiagId;
+ if (LangOpts.C2y)
+ DiagId = diag::warn_c2y_compat_octal_literal;
+ else
+ DiagId = diag::ext_octal_literal;
+ Diags.Report(TokLoc, DiagId);
+ ++s;
+ DigitsBegin = s;
+ SawOctalPrefix = true;
+ }
+
+ auto _ = llvm::make_scope_exit([&] {
+ // If we still have an octal value but we did not see an octal prefix,
+ // diagnose as being an obsolescent feature starting in C2y.
+ if (radix == 8 && LangOpts.C2y && !SawOctalPrefix && !hadError)
+ Diags.Report(TokLoc, diag::warn_unprefixed_octal_deprecated);
+ });
+
// For now, the radix is set to 8. If we discover that we have a
// floating point constant, the radix will change to 10. Octal floating
// point constants are not permitted (only decimal and hexadecimal).
diff --git a/clang/test/C/C2y/n3353.c b/clang/test/C/C2y/n3353.c
new file mode 100644
index 0000000000000..4e8b7f0fc43ba
--- /dev/null
+++ b/clang/test/C/C2y/n3353.c
@@ -0,0 +1,95 @@
+// RUN: %clang_cc1 -verify=expected,c2y -pedantic -std=c2y %s
+// RUN: %clang_cc1 -verify=expected,c2y,compat -Wpre-c2y-compat -std=c2y %s
+// RUN: %clang_cc1 -verify=expected,ext -pedantic -std=c23 %s
+// RUN: %clang_cc1 -verify=expected,ext -pedantic -x c++ -Wno-c11-extensions %s
+
+
+/* WG14 N3353: Clang 21
+ * Obsolete implicitly octal literals and add delimited escape sequences
+ */
+
+constexpr int i = 0234; // c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+constexpr int j = 0o234; /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+
+static_assert(i == 156);
+static_assert(j == 156);
+
+// Show that 0O is the same as Oo (tested above)
+static_assert(0O1234 == 0o1234); /* ext-warning 2 {{octal integer literals are a C2y extension}}
+ compat-warning 2 {{octal integer literals are incompatible with standards before C2y}}
+ */
+
+// Demonstrate that it works fine in the preprocessor.
+#if 0o123 != 0x53 /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+#error "oh no, math stopped working!"
+#endif
+
+// 0 by itself is not deprecated, of course.
+int k = 0;
+
+// Make sure there are no surprises with auto and type deduction. Promotion
+// turns this into an 'int', and 'constexpr' implies 'const'.
+constexpr auto l = 0o1234567; /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+static_assert(l == 0x53977);
+static_assert(__extension__ _Generic(typeof(0o1), typeof(01) : 1, default : 0)); /* c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+ compat-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+static_assert(__extension__ _Generic(typeof(l), const int : 1, default : 0)); // compat-warning {{passing a type argument as the first operand to '_Generic' is incompatible with C standards before C2y}}
+
+// Note that 0o by itself is an invalid literal.
+int m = 0o; /* expected-error {{invalid suffix 'o' on integer constant}}
+ c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+ */
+
+// Ensure negation works as expected.
+static_assert(-0o1234 == -668); /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+
+// FIXME: it would be better to not diagnose the compat and ext warnings when
+// the octal literal is invalid.
+// We expect diagnostics for non-octal digits.
+int n = 0o18; /* expected-error {{invalid digit '8' in octal constant}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ ext-warning {{octal integer literals are a C2y extension}}
+ */
+int o1 = 0o8; /* expected-error {{invalid suffix 'o8' on integer constant}}
+ c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+ */
+// FIXME: however, it matches the behavior for hex literals in terms of the
+// error reported. Unfortunately, we then go on to think 0 is an octal literal
+// without a prefix, which is again a bit confusing.
+int o2 = 0xG; /* expected-error {{invalid suffix 'xG' on integer constant}}
+ c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+ */
+
+// Ensure digit separators work as expected.
+constexpr int p = 0o0'1'2'3'4'5'6'7; /* compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ ext-warning {{octal integer literals are a C2y extension}}
+ */
+static_assert(p == 01234567); // c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+int q = 0o'0'1; /* expected-error {{invalid suffix 'o'0'1' on integer constant}}
+ c2y-warning {{octal literals without a '0o' prefix are deprecated}}
+ */
+
+#ifdef __cplusplus
+template <unsigned N>
+struct S {
+ static_assert(N == 0o567); /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+};
+
+void foo() {
+ S<0o567> s; /* ext-warning {{octal integer literals are a C2y extension}}
+ compat-warning {{octal integer literals are incompatible with standards before C2y}}
+ */
+}
+#endif
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index d68e8d6441ed2..7cf50bfdb6639 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -176,7 +176,7 @@ <h2 id="c2y">C2y implementation status</h2>
<tr>
<td>Obsolete implicitly octal literals and add delimited escape sequences</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3353.htm">N3353</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<tr>
<td>'if' declarations, v2</td>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on that Aaron
✅ With the latest revision this PR passed the C/C++ code formatter. |
/// Diagnose use of a delimited or named escape sequence. | ||
static void DiagnoseDelimitedOrNamedEscapeSequence(SourceLocation Loc, | ||
bool Named, | ||
const LangOptions &Opts, | ||
DiagnosticsEngine &Diags); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could just be a non-member static function Lexer.cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's called from LiteralSupport.cpp, so it could be a non-member function, but I think it's a bit cleaner to scope it to the Lexer
to make it clear this is emitting lexer-based diagnostics. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Aaron!
WG14 N3353 added support for 0o and 0O as octal literal prefixes. It also deprecates use of octal literals without a prefix, except for the literal 0.
This feature is being exposed as an extension in older C language modes as well as in all C++ language modes.